Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/synchronize/SyncInfoTree.java31
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/core/variants/CachedResourceVariant.java2
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/ResourceVariantCache.java7
-rw-r--r--bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/subscribers/SubscriberResourceMappingContext.java155
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java55
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java2
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolderTree.java17
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/CVSResourceVariantTree.java36
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CVSMergeContext.java72
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/UpdateModelAction.java86
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/WorkspaceTraversalAction.java48
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheBaseContentsOperation.java63
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheRemoteContentsOperation.java56
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheTreeContentsOperation.java126
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/SingleCommandOperation.java12
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/WorkspaceResourceMapper.java3
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/RefreshDirtyStateOperation.java94
-rw-r--r--bundles/org.eclipse.team.ui/META-INF/MANIFEST.MF2
-rw-r--r--bundles/org.eclipse.team.ui/plugin.xml5
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ResourceMappingContentProvider.java106
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ResourceNavigatorContentExtensionFactory.java41
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamAdapterFactory.java7
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/AdditionalMappingsDialog.java82
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/MappingSelectionDialog.java2
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingHierarchyArea.java278
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingLabelProvider.java48
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingResourceDisplayArea.java22
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingSelectionArea.java123
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/DefaultResourceMappingMerger.java74
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/INavigatorContentExtensionFactory.java37
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/IResourceMappingContentProvider.java36
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/NavigatorContentExtension.java72
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingCheckinOperation.java95
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingLoadOperation.java63
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingScope.java93
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/SimpleResourceMappingOperationInput.java152
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/SynchronizationOperationLabelProvider.java123
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IDisposeListener.java32
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IMergeContext.java113
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IResourceMappingMerger.java98
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IResourceMappingOperationInput.java100
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ISynchronizationContext.java158
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ISynchronizeOperationContext.java68
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ITeamViewerContext.java53
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/MergeContext.java399
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/MergeStatus.java88
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingMergeOperation.java187
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingOperation.java150
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingOperationInput.java251
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/SynchronizeOperationContext.java122
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/TeamViewerContext.java61
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/AbstractSynchronizeScope.java30
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/ISynchronizeScope.java25
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/launchConfigurations/CVS UI Tests.launch26
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/EclipseTest.java10
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/ResourceMapperTests.java120
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/SyncInfoSetTraveralContext.java36
57 files changed, 4213 insertions, 240 deletions
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/synchronize/SyncInfoTree.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/synchronize/SyncInfoTree.java
index 353830cc6..7e4ed3c28 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/synchronize/SyncInfoTree.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/synchronize/SyncInfoTree.java
@@ -13,6 +13,7 @@ package org.eclipse.team.core.synchronize;
import java.util.*;
import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
import org.eclipse.core.runtime.*;
import org.eclipse.team.internal.core.Messages;
import org.eclipse.team.internal.core.TeamPlugin;
@@ -46,6 +47,11 @@ public class SyncInfoTree extends SyncInfoSet {
*/
public SyncInfoTree(SyncInfo[] infos) {
super(infos);
+ for (int i = 0; i < infos.length; i++) {
+ SyncInfo info = infos[i];
+ IResource local = info.getLocal();
+ addToParents(local, local);
+ }
}
/**
@@ -350,4 +356,29 @@ public class SyncInfoTree extends SyncInfoSet {
return (IResource[]) children.toArray(new IResource[children.size()]);
}
+ /**
+ * Return the sync info contained in this set that are contained
+ * in the given traversals.
+ * @param traversals the traversals
+ * @return the sync info contained in this set that are contained
+ * in the given traversals
+ * @since 3.2
+ */
+ public SyncInfo[] getSyncInfos(ResourceTraversal[] traversals) {
+ SyncInfoSet set = new SyncInfoSet();
+ for (int i = 0; i < traversals.length; i++) {
+ ResourceTraversal traversal = traversals[i];
+ IResource[] resources = traversal.getResources();
+ for (int j = 0; j < resources.length; j++) {
+ IResource resource = resources[j];
+ SyncInfo[] infos = getSyncInfos(resource, traversal.getDepth());
+ for (int k = 0; k < infos.length; k++) {
+ SyncInfo info = infos[k];
+ set.add(info);
+ }
+ }
+ }
+ return set.getSyncInfos();
+ }
+
}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/variants/CachedResourceVariant.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/variants/CachedResourceVariant.java
index 383885a4a..5d5a72e01 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/core/variants/CachedResourceVariant.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/core/variants/CachedResourceVariant.java
@@ -151,7 +151,7 @@ public abstract class CachedResourceVariant extends PlatformObject implements IR
* <p>
* This method is not intended to be overridden by clients.
*/
- protected boolean isContentsCached() {
+ public boolean isContentsCached() {
if (isContainer() || !isHandleCached()) {
return false;
}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/ResourceVariantCache.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/ResourceVariantCache.java
index 9cb32f7d3..fedd69bbf 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/ResourceVariantCache.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/ResourceVariantCache.java
@@ -240,5 +240,12 @@ public class ResourceVariantCache {
public String getName() {
return name;
}
+
+ /*
+ * Method used for testing only
+ */
+ public ResourceVariantCacheEntry[] getEntries() {
+ return (ResourceVariantCacheEntry[]) cacheEntries.values().toArray(new ResourceVariantCacheEntry[cacheEntries.size()]);
+ }
}
diff --git a/bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/subscribers/SubscriberResourceMappingContext.java b/bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/subscribers/SubscriberResourceMappingContext.java
index 6eb6582dc..d1cbbbb0a 100644
--- a/bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/subscribers/SubscriberResourceMappingContext.java
+++ b/bundles/org.eclipse.team.core/src/org/eclipse/team/internal/core/subscribers/SubscriberResourceMappingContext.java
@@ -10,17 +10,29 @@
*******************************************************************************/
package org.eclipse.team.internal.core.subscribers;
-import java.util.*;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
-import org.eclipse.core.resources.*;
-import org.eclipse.core.resources.mapping.*;
-import org.eclipse.core.runtime.*;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.resources.mapping.RemoteResourceMappingContext;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.subscribers.Subscriber;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.core.synchronize.SyncInfoFilter;
import org.eclipse.team.core.variants.IResourceVariant;
-import org.eclipse.team.internal.core.*;
+import org.eclipse.team.internal.core.Messages;
+import org.eclipse.team.internal.core.Policy;
+import org.eclipse.team.internal.core.TeamPlugin;
/**
* A resource mapping context that provides the client access to the remote state
@@ -28,6 +40,8 @@ import org.eclipse.team.internal.core.*;
* to determine whether the local contents differ from the remote contents.
* This allows the context to be used for different operations (check-in,
* update and replace).
+ *
+ * TODO: Do we want explicit support for differentiating a commit from an update?
* @since 3.1
*/
public class SubscriberResourceMappingContext extends RemoteResourceMappingContext {
@@ -36,14 +50,15 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
private final SyncInfoFilter contentDiffFilter;
// Lists used to keep track of resources that have been refreshed
- Set shallowRefresh = new HashSet();
- Set deepRefresh = new HashSet();
+ private Set shallowRefresh = new HashSet();
+ private Set deepRefresh = new HashSet();
+ private boolean autoRefresh;
/**
* Return a resource mapping context suitable for a replace operations.
* @return a resource mapping context suitable for a replace operations
*/
- public static ResourceMappingContext getReplaceContext(Subscriber subscriber) {
+ public static RemoteResourceMappingContext getReplaceContext(Subscriber subscriber) {
return new SubscriberResourceMappingContext(subscriber, new SyncInfoFilter() {
public boolean select(SyncInfo info, IProgressMonitor monitor) {
if (info != null) {
@@ -53,8 +68,7 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
}
return false;
}
-
- });
+ }, true);
}
/**
@@ -63,7 +77,7 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
* server to update the local workspace resources.
* @return a resource mapping context suitable for a update operations
*/
- public static ResourceMappingContext getUpdateContext(Subscriber subscriber) {
+ public static RemoteResourceMappingContext getUpdateContext(Subscriber subscriber) {
return new SubscriberResourceMappingContext(subscriber, new SyncInfoFilter() {
public boolean select(SyncInfo info, IProgressMonitor monitor) {
if (info != null) {
@@ -73,8 +87,7 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
}
return false;
}
-
- });
+ }, true);
}
/**
@@ -83,7 +96,7 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
* server from the local workspace resources, typically creating a new version of the resource.
* @return a resource mapping context suitable for a check-in operations
*/
- public static ResourceMappingContext getCheckInContext(Subscriber subscriber) {
+ public static RemoteResourceMappingContext getCheckInContext(Subscriber subscriber) {
return new SubscriberResourceMappingContext(subscriber, new SyncInfoFilter() {
public boolean select(SyncInfo info, IProgressMonitor monitor) {
if (info != null) {
@@ -93,8 +106,7 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
}
return false;
}
-
- });
+ }, true);
}
/**
@@ -103,7 +115,7 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
* that differ.
* @return a resource mapping context suitable for compare operations
*/
- public static ResourceMappingContext getCompareContext(Subscriber subscriber) {
+ public static RemoteResourceMappingContext getCompareContext(Subscriber subscriber) {
return new SubscriberResourceMappingContext(subscriber, new SyncInfoFilter() {
public boolean select(SyncInfo info, IProgressMonitor monitor) {
if (info != null) {
@@ -111,8 +123,7 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
}
return false;
}
-
- });
+ }, true);
}
/**
@@ -121,30 +132,43 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
* @param contentDiffFilter filter that is used to determine if the remote contents differ
* from the local contents
*/
- public SubscriberResourceMappingContext(Subscriber subscriber, SyncInfoFilter contentDiffFilter) {
+ public SubscriberResourceMappingContext(Subscriber subscriber, SyncInfoFilter contentDiffFilter, boolean autoRefresh) {
this.subscriber = subscriber;
this.contentDiffFilter = contentDiffFilter;
+ this.autoRefresh = autoRefresh;
}
- /* (non-Javadoc)
- * @see org.eclipse.core.resources.mapping.ResourceMappingContext#contentDiffers(org.eclipse.core.resources.IFile, org.eclipse.core.runtime.IProgressMonitor)
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.resources.mapping.RemoteResourceMappingContext#hasRemoteChange(org.eclipse.core.resources.IResource, org.eclipse.core.runtime.IProgressMonitor)
*/
- public final boolean contentDiffers(IFile file, IProgressMonitor monitor) throws CoreException {
+ public final boolean hasRemoteChange(IResource resource, IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(null, 100);
- ensureRefreshed(file, IResource.DEPTH_ZERO, NONE, Policy.subMonitorFor(monitor, 10));
- SyncInfo syncInfo = subscriber.getSyncInfo(file);
- validateRemote(file, syncInfo);
- return syncInfo != null && contentDiffFilter.select(syncInfo, Policy.subMonitorFor(monitor, 90));
+ ensureRefreshed(resource, IResource.DEPTH_ONE, NONE, monitor);
+ SyncInfo syncInfo = subscriber.getSyncInfo(resource);
+ validateRemote(resource, syncInfo);
+ if (syncInfo == null) return false;
+ int direction = SyncInfo.getDirection(syncInfo.getKind());
+ return direction == SyncInfo.OUTGOING || direction == SyncInfo.CONFLICTING;
} finally {
monitor.done();
}
}
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.resources.mapping.RemoteResourceMappingContext#hasLocalChange(org.eclipse.core.resources.IResource, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public boolean hasLocalChange(IResource resource, IProgressMonitor monitor) throws CoreException {
+ SyncInfo syncInfo = subscriber.getSyncInfo(resource);
+ if (syncInfo == null) return false;
+ int direction = SyncInfo.getDirection(syncInfo.getKind());
+ return direction == SyncInfo.OUTGOING || direction == SyncInfo.CONFLICTING;
+ }
/* (non-Javadoc)
* @see org.eclipse.core.resources.mapping.ResourceMappingContext#fetchContents(org.eclipse.core.resources.IFile, org.eclipse.core.runtime.IProgressMonitor)
*/
- public final IStorage fetchContents(IFile file, IProgressMonitor monitor) throws CoreException {
+ public final IStorage fetchRemoteContents(IFile file, IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(null, 100);
ensureRefreshed(file, IResource.DEPTH_ZERO, FILE_CONTENTS_REQUIRED, Policy.subMonitorFor(monitor, 10));
@@ -158,6 +182,24 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
monitor.done();
}
}
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.internal.resources.mapping.RemoteResourceMappingContext#fetchBaseContents(org.eclipse.core.resources.IFile, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public final IStorage fetchBaseContents(IFile file, IProgressMonitor monitor) throws CoreException {
+ try {
+ monitor.beginTask(null, 100);
+ ensureRefreshed(file, IResource.DEPTH_ZERO, FILE_CONTENTS_REQUIRED, Policy.subMonitorFor(monitor, 10));
+ SyncInfo syncInfo = subscriber.getSyncInfo(file);
+ IResourceVariant base = validateBase(file, syncInfo);
+ if (base == null) {
+ return null;
+ }
+ return base.getStorage(Policy.subMonitorFor(monitor, 90));
+ } finally {
+ monitor.done();
+ }
+ }
/* (non-Javadoc)
* @see org.eclipse.core.resources.mapping.ResourceMappingContext#fetchMembers(org.eclipse.core.resources.IContainer, org.eclipse.core.runtime.IProgressMonitor)
@@ -213,7 +255,7 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
* flag is passed. It is up to subclass to handle this.
* @param resources the resources to be refreshed
* @param depth the depth of the refresh
- * @param flags the flags that indicate extra state that shoudl be fetched
+ * @param flags the flags that indicate extra state that should be fetched
* @param monitor a progress monitor
* @throws TeamException
*/
@@ -246,18 +288,20 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
* since the context has been created.
*/
private void ensureRefreshed(IResource resource, int depth, int flags, IProgressMonitor monitor) throws TeamException {
- if (depth == IResource.DEPTH_INFINITE) {
- // If the resource or a parent was refreshed deeply, no need to do it again
- if (wasRefreshedDeeply(resource))
- return;
- // if the resource is a file, a shallow refresh is enough
- if (resource.getType() == IResource.FILE && wasRefreshedShallow(resource))
- return;
- } else {
- if (wasRefreshedShallow(resource))
- return;
- }
- refresh(new IResource[] { resource }, depth, flags, monitor);
+ if (autoRefresh) {
+ if (depth == IResource.DEPTH_INFINITE) {
+ // If the resource or a parent was refreshed deeply, no need to do it again
+ if (wasRefreshedDeeply(resource))
+ return;
+ // if the resource is a file, a shallow refresh is enough
+ if (resource.getType() == IResource.FILE && wasRefreshedShallow(resource))
+ return;
+ } else {
+ if (wasRefreshedShallow(resource))
+ return;
+ }
+ refresh(new IResource[] { resource }, depth, flags, monitor);
+ }
}
/*
@@ -294,12 +338,39 @@ public class SubscriberResourceMappingContext extends RemoteResourceMappingConte
if (syncInfo == null) return null;
IResourceVariant remote = syncInfo.getRemote();
if (remote == null) return null;
- boolean containerExpected = resource.getType() != IResource.FILE;
+ return validateRemote(resource, remote);
+ }
+
+ private IResourceVariant validateRemote(IResource resource, IResourceVariant remote) throws CoreException {
+ boolean containerExpected = resource.getType() != IResource.FILE;
if (remote.isContainer() && !containerExpected) {
throw new CoreException(new Status(IStatus.ERROR, TeamPlugin.ID, IResourceStatus.RESOURCE_WRONG_TYPE, Messages.SubscriberResourceMappingContext_0 + resource.getFullPath().toString(), null));
} else if (!remote.isContainer() && containerExpected) {
throw new CoreException(new Status(IStatus.ERROR, TeamPlugin.ID, IResourceStatus.RESOURCE_WRONG_TYPE, Messages.SubscriberResourceMappingContext_1 + resource.getFullPath().toString(), null));
}
return remote;
+ }
+
+ /*
+ * Validate that the base resource is of the proper type and return the
+ * base resource if it is OK. A return of null indicates that there is no base.
+ */
+ private IResourceVariant validateBase(IResource resource, SyncInfo syncInfo) throws CoreException {
+ if (syncInfo == null) return null;
+ IResourceVariant base = syncInfo.getBase();
+ if (base == null) return null;
+ return validateRemote(resource, base);
+ }
+
+ public void setAutoRefresh(boolean autoRefresh) {
+ this.autoRefresh = autoRefresh;
}
+
+ public boolean isThreeWay() {
+ return subscriber.getResourceComparator().isThreeWay();
+ }
+
+ public boolean contentDiffers(IFile file, IProgressMonitor monitor) throws CoreException {
+ return hasRemoteChange(file, monitor) || hasLocalChange(file, monitor);
+ }
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java
index 4f4d35400..edc591bc3 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSWorkspaceSubscriber.java
@@ -13,36 +13,18 @@ package org.eclipse.team.internal.ccvs.core;
import java.util.ArrayList;
import java.util.List;
-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.IResourceVisitor;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
import org.eclipse.osgi.util.NLS;
-import org.eclipse.team.core.ITeamStatus;
-import org.eclipse.team.core.RepositoryProvider;
-import org.eclipse.team.core.TeamException;
-import org.eclipse.team.core.TeamStatus;
+import org.eclipse.team.core.*;
import org.eclipse.team.core.subscribers.ISubscriberChangeEvent;
import org.eclipse.team.core.subscribers.SubscriberChangeEvent;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.core.synchronize.SyncInfoSet;
-import org.eclipse.team.core.variants.IResourceVariant;
-import org.eclipse.team.core.variants.IResourceVariantTree;
-import org.eclipse.team.core.variants.PersistantResourceVariantByteStore;
-import org.eclipse.team.core.variants.ResourceVariantByteStore;
-import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
+import org.eclipse.team.core.variants.*;
import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer;
-import org.eclipse.team.internal.ccvs.core.resources.RemoteFolderTreeBuilder;
-import org.eclipse.team.internal.ccvs.core.syncinfo.CVSBaseResourceVariantTree;
-import org.eclipse.team.internal.ccvs.core.syncinfo.CVSDescendantResourceVariantByteStore;
-import org.eclipse.team.internal.ccvs.core.syncinfo.CVSResourceVariantTree;
+import org.eclipse.team.internal.ccvs.core.syncinfo.*;
import org.eclipse.team.internal.ccvs.core.util.ResourceStateChangeListeners;
/**
@@ -277,13 +259,12 @@ public class CVSWorkspaceSubscriber extends CVSSyncTreeSubscriber implements IRe
*/
public void updateRemote(CVSTeamProvider provider, ICVSFolder folder, boolean recurse, IProgressMonitor monitor) throws TeamException {
try {
- monitor.beginTask(null, 100);
+ monitor.beginTask(null, IProgressMonitor.UNKNOWN);
IResource resource = folder.getIResource();
if (resource != null) {
- ICVSResource tree = RemoteFolderTreeBuilder.buildBaseTree(
- (CVSRepositoryLocation)provider.getRemoteLocation(),
- folder,
- null,
+ ICVSResource tree = buildBaseTree(
+ resource,
+ false,
Policy.subMonitorFor(monitor, 50));
setRemote(resource, (IResourceVariant)tree, Policy.subMonitorFor(monitor, 50));
}
@@ -291,5 +272,23 @@ public class CVSWorkspaceSubscriber extends CVSSyncTreeSubscriber implements IRe
monitor.done();
}
}
+
+ public ICVSRemoteResource buildBaseTree(IResource resource, boolean immutable, IProgressMonitor monitor) throws TeamException {
+ try {
+ monitor.beginTask(null, IProgressMonitor.UNKNOWN);
+ return ((CVSResourceVariantTree)getBaseTree()).buildTree(null, resource, immutable, monitor);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ public ICVSRemoteResource buildRemoteTree(IResource resource, boolean immutable, IProgressMonitor monitor) throws TeamException {
+ try {
+ monitor.beginTask(null, IProgressMonitor.UNKNOWN);
+ return ((CVSResourceVariantTree)getRemoteTree()).buildTree(null, resource, immutable, monitor);
+ } finally {
+ monitor.done();
+ }
+ }
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
index f6a7e5698..6f3a3e8c7 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFile.java
@@ -132,7 +132,7 @@ public class RemoteFile extends RemoteResource implements ICVSRemoteFile {
return newInfo.getBytes();
}
- /* package */ RemoteFile(RemoteFolder parent, byte[] syncBytes) throws CVSException {
+ public RemoteFile(RemoteFolder parent, byte[] syncBytes) throws CVSException {
this(parent, Update.STATE_NONE, syncBytes);
}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolderTree.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolderTree.java
index 49230bbf7..b111ed527 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolderTree.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/resources/RemoteFolderTree.java
@@ -11,6 +11,7 @@
package org.eclipse.team.internal.ccvs.core.resources;
+import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.team.internal.ccvs.core.CVSException;
import org.eclipse.team.internal.ccvs.core.CVSTag;
@@ -18,6 +19,9 @@ import org.eclipse.team.internal.ccvs.core.ICVSRemoteResource;
import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.ICVSResource;
import org.eclipse.team.internal.ccvs.core.ICVSResourceVisitor;
+import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.core.util.KnownRepositories;
/**
* Whereas the RemoteFolder class provides access to a remote hierarchy using
@@ -26,12 +30,23 @@ import org.eclipse.team.internal.ccvs.core.ICVSResourceVisitor;
*/
public class RemoteFolderTree extends RemoteFolder {
+ public static RemoteFolderTree fromBytes(RemoteFolderTree parent, IResource local, byte[] bytes) throws CVSException {
+ Assert.isNotNull(bytes);
+ Assert.isTrue(local.getType() != IResource.FILE);
+ FolderSyncInfo syncInfo = FolderSyncInfo.getFolderSyncInfo(bytes);
+ return new RemoteFolderTree(parent, local.getName(), KnownRepositories.getInstance().getRepository(syncInfo.getRoot()), syncInfo.getRepository(), syncInfo.getTag(), syncInfo.getIsStatic());
+ }
+
public RemoteFolderTree(RemoteFolder parent, ICVSRepositoryLocation repository, String repositoryRelativePath, CVSTag tag) {
super(parent, repository, repositoryRelativePath, tag);
}
public RemoteFolderTree(RemoteFolder parent, String name, ICVSRepositoryLocation repository, String repositoryRelativePath, CVSTag tag) {
- super(parent, name, repository, repositoryRelativePath, tag, false);
+ this(parent, name, repository, repositoryRelativePath, tag, false);
+ }
+
+ public RemoteFolderTree(RemoteFolder parent, String name, ICVSRepositoryLocation repository, String repositoryRelativePath, CVSTag tag, boolean isStatic) {
+ super(parent, name, repository, repositoryRelativePath, tag, isStatic);
}
/*
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/CVSResourceVariantTree.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/CVSResourceVariantTree.java
index 8478e5af9..bd1dcef47 100644
--- a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/CVSResourceVariantTree.java
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/syncinfo/CVSResourceVariantTree.java
@@ -352,4 +352,40 @@ public class CVSResourceVariantTree extends ResourceVariantTree {
}
return false;
}
+
+
+ public ICVSRemoteResource buildTree(RemoteFolderTree parent, IResource resource, boolean immutable, IProgressMonitor monitor) throws TeamException {
+
+ Policy.checkCanceled(monitor);
+
+ byte[] remoteBytes = getByteStore().getBytes(resource);
+ if (remoteBytes == null) {
+ // There is no remote handle for this resource
+ return null;
+ }
+
+ if (resource.getType() == IResource.FILE) {
+ if (immutable) {
+ remoteBytes = ResourceSyncInfo.setTag(remoteBytes, new CVSTag(ResourceSyncInfo.getRevision(remoteBytes), CVSTag.VERSION));
+ }
+ if (parent == null) {
+ return (ICVSRemoteResource)getResourceVariant(resource);
+ }
+ return new RemoteFile(parent, remoteBytes);
+ } else {
+ RemoteFolderTree remote = RemoteFolderTree.fromBytes(parent, resource, remoteBytes);
+ IResource[] members = members(resource);
+ List children = new ArrayList();
+ for (int i = 0; i < members.length; i++) {
+ IResource member = members[i];
+ ICVSRemoteResource child = buildTree(remote, member, immutable, monitor);
+ if (child != null)
+ children.add(child);
+ }
+
+ // Add the children to the remote folder tree
+ remote.setChildren((ICVSRemoteResource[])children.toArray(new ICVSRemoteResource[children.size()]));
+ return remote;
+ }
+ }
}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CVSMergeContext.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CVSMergeContext.java
new file mode 100644
index 000000000..b5cec8d68
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/CVSMergeContext.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
+import org.eclipse.core.runtime.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.internal.ccvs.core.CVSSyncInfo;
+import org.eclipse.team.internal.ccvs.ui.subscriber.WorkspaceSynchronizeParticipant;
+import org.eclipse.team.internal.ui.mapping.ResourceMappingScope;
+import org.eclipse.team.ui.mapping.*;
+import org.eclipse.team.ui.synchronize.ISynchronizeScope;
+
+public class CVSMergeContext extends MergeContext {
+
+ private WorkspaceSynchronizeParticipant participant;
+
+ public static IMergeContext createContext(IResourceMappingOperationInput input, IProgressMonitor monitor) {
+ WorkspaceSynchronizeParticipant participant = new WorkspaceSynchronizeParticipant(input.asSynchronizationScope());
+ participant.refreshNow(participant.getResources(), NLS.bind("Preparing to merge {0}", new String[] { "TODO: mapping description for CVS merge context initialization" }), monitor);
+ return new CVSMergeContext(THREE_WAY, participant, input);
+ }
+
+ protected CVSMergeContext(String type, WorkspaceSynchronizeParticipant participant, IResourceMappingOperationInput input) {
+ super(type, participant.getSyncInfoSet(), input);
+ this.participant = participant;
+ }
+
+ public IStatus markAsMerged(IFile file, IProgressMonitor monitor) {
+ try {
+ SyncInfo info = getSyncInfoTree().getSyncInfo(file);
+ if (info instanceof CVSSyncInfo) {
+ CVSSyncInfo cvsInfo = (CVSSyncInfo) info;
+ cvsInfo.makeOutgoing(monitor);
+ }
+ return Status.OK_STATUS;
+ } catch (TeamException e) {
+ return e.getStatus();
+ }
+ }
+
+ public void dispose() {
+ participant.dispose();
+ super.dispose();
+ }
+
+ public SyncInfo getSyncInfo(IResource resource) throws CoreException {
+ return participant.getSubscriber().getSyncInfo(resource);
+ }
+
+ public void refresh(ResourceTraversal[] traversals, int flags, IProgressMonitor monitor) throws CoreException {
+ IResource[] resources = new ResourceMappingScope("", getResourceMappings(ALL_MAPPINGS), traversals).getRoots();
+ participant.refreshNow(resources, "TODO: CVS Merge Context Refresh", monitor);
+ }
+
+ public ISynchronizeScope getScope() {
+ return (ResourceMappingScope)participant.getScope();
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/UpdateModelAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/UpdateModelAction.java
new file mode 100644
index 000000000..f990a6cbd
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/UpdateModelAction.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.actions;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.operations.CacheBaseContentsOperation;
+import org.eclipse.team.internal.ccvs.ui.operations.CacheRemoteContentsOperation;
+import org.eclipse.team.ui.mapping.*;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * Action that runs an update without prompting the user for a tag.
+ *
+ * @since 3.1
+ */
+public class UpdateModelAction extends WorkspaceTraversalAction {
+
+ private class CVSMergeOperation extends ResourceMappingMergeOperation {
+
+ protected CVSMergeOperation(IWorkbenchPart part, IResourceMappingOperationInput input) {
+ super(part, input);
+ }
+
+ protected IMergeContext buildMergeContext(IProgressMonitor monitor) {
+ monitor.beginTask(null, 100);
+ IMergeContext context = CVSMergeContext.createContext(getInput(), Policy.subMonitorFor(monitor, 50));
+ // cache the base and remote contents
+ // TODO: Refreshing and caching now takes 3 round trips.
+ // OPTIMIZE: remote state and contents could be obtained in 1
+ // OPTIMIZE: Based could be avoided if we always cached base locally
+ try {
+ new CacheBaseContentsOperation(getPart(), getInput().getInputMappings(), context.getSyncInfoTree(), true).run(Policy.subMonitorFor(monitor, 25));
+ new CacheRemoteContentsOperation(getPart(), getInput().getInputMappings(), context.getSyncInfoTree()).run(Policy.subMonitorFor(monitor, 25));
+ } catch (InvocationTargetException e) {
+ CVSUIPlugin.log(CVSException.wrapException(e));
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ monitor.done();
+ return context;
+ }
+
+ protected void requiresManualMerge(ModelProvider[] providers, IMergeContext context) throws CoreException {
+ // TODO Auto-generated method stub
+ }
+
+ }
+
+ /*
+ * @see org.eclipse.team.internal.ccvs.ui.actions.WorkspaceAction#isEnabledForAddedResources()
+ */
+ protected boolean isEnabledForAddedResources() {
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.actions.WorkspaceAction#isEnabledForNonExistantResources()
+ */
+ protected boolean isEnabledForNonExistantResources() {
+ return true;
+ }
+
+ public void execute(IAction action) throws InterruptedException, InvocationTargetException {
+ new CVSMergeOperation(getTargetPart(), getOperationInput()).run();
+ }
+
+ public String getId() {
+ return "org.eclipse.team.cvs.ui.modelupdate";
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/WorkspaceTraversalAction.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/WorkspaceTraversalAction.java
index afe30be50..96a022919 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/WorkspaceTraversalAction.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/actions/WorkspaceTraversalAction.java
@@ -15,13 +15,17 @@ import java.util.*;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.mapping.*;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.subscribers.Subscriber;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
import org.eclipse.team.internal.core.subscribers.SubscriberResourceMappingContext;
+import org.eclipse.team.internal.ui.dialogs.AdditionalMappingsDialog;
+import org.eclipse.team.internal.ui.mapping.SimpleResourceMappingOperationInput;
+import org.eclipse.team.ui.mapping.*;
import org.eclipse.ui.PlatformUI;
@@ -38,9 +42,37 @@ public abstract class WorkspaceTraversalAction extends WorkspaceAction {
* within a CVS managed project
*/
protected ResourceMapping[] getCVSResourceMappings() {
- return getSelectedResourceMappings(CVSProviderPlugin.getTypeId());
+ ResourceMapping[] selectedMappings = getSelectedResourceMappings(CVSProviderPlugin.getTypeId());
+ try {
+ IResourceMappingOperationInput input = new ResourceMappingOperationInput(selectedMappings, ResourceMappingContext.LOCAL_CONTEXT);
+ input.buildInput(new NullProgressMonitor());
+ if (input.hasAdditionalMappings()) {
+ ResourceMapping[] allMappings = input.getInputMappings();
+ return showAllMappings(selectedMappings, allMappings);
+ }
+ } catch (CoreException e) {
+ CVSUIPlugin.log(e);
+ }
+ return selectedMappings;
}
+ private ResourceMapping[] showAllMappings(final ResourceMapping[] selectedMappings, final ResourceMapping[] allMappings) {
+ final boolean[] canceled = new boolean[] { false };
+ getShell().getDisplay().syncExec(new Runnable() {
+ public void run() {
+ AdditionalMappingsDialog dialog = new AdditionalMappingsDialog(getShell(), "Participating Elements", selectedMappings, new TeamViewerContext(allMappings));
+ int result = dialog.open();
+ canceled[0] = result != Dialog.OK;
+ }
+
+ });
+
+ if (canceled[0]) {
+ return new ResourceMapping[0];
+ }
+ return allMappings;
+ }
+
protected static IResource[] getRootTraversalResources(ResourceMapping[] mappings, ResourceMappingContext context, IProgressMonitor monitor) throws CoreException {
List result = new ArrayList();
for (int i = 0; i < mappings.length; i++) {
@@ -68,7 +100,15 @@ public abstract class WorkspaceTraversalAction extends WorkspaceAction {
return getResourcesToCompare(getCVSResourceMappings(), subscriber);
}
- public static IResource[] getResourcesToCompare(final ResourceMapping[] mappings, final Subscriber subscriber) throws InvocationTargetException {
+ protected ResourceMappingContext getResourceMappingContext() {
+ return SubscriberResourceMappingContext.getCompareContext(CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber());
+ }
+
+ protected SimpleResourceMappingOperationInput getOperationInput() {
+ return new SimpleResourceMappingOperationInput(getSelectedResourceMappings(CVSProviderPlugin.getTypeId()), getResourceMappingContext());
+ }
+
+ public static IResource[] getResourcesToCompare(final ResourceMapping[] mappings, final Subscriber subscriber) throws InvocationTargetException {
// Determine what resources need to be synchronized.
// Use a resource mapping context to include any relevant remote resources
final IResource[][] resources = new IResource[][] { null };
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheBaseContentsOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheBaseContentsOperation.java
new file mode 100644
index 000000000..4b0e08ddc
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheBaseContentsOperation.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.operations;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.synchronize.SyncInfoTree;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * Operation that ensures that the contents for base
+ * of each local resource is cached.
+ */
+public class CacheBaseContentsOperation extends CacheTreeContentsOperation {
+
+ private final boolean includeOutgoing;
+
+ public CacheBaseContentsOperation(IWorkbenchPart part, ResourceMapping[] mappers, SyncInfoTree tree, boolean includeOutgoing) {
+ super(part, mappers, tree);
+ this.includeOutgoing = includeOutgoing;
+ }
+
+ protected boolean needsContents(SyncInfo info) {
+ IResource local = info.getLocal();
+ IResourceVariant base = info.getBase();
+ if (base != null && local.getType() == IResource.FILE) {
+ int direction = SyncInfo.getDirection(info.getKind());
+ if (isEnabledForDirection(direction)) {
+ if (base instanceof RemoteFile) {
+ RemoteFile remote = (RemoteFile) base;
+ if (!remote.isContentsCached()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private boolean isEnabledForDirection(int direction) {
+ return direction == SyncInfo.CONFLICTING ||
+ (includeOutgoing && direction == SyncInfo.OUTGOING);
+ }
+
+ protected ICVSRemoteResource buildTree(CVSTeamProvider provider) throws TeamException {
+ return CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber().buildBaseTree(provider.getProject(), true, new NullProgressMonitor());
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheRemoteContentsOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheRemoteContentsOperation.java
new file mode 100644
index 000000000..73a0d0e8c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheRemoteContentsOperation.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.operations;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.synchronize.SyncInfoTree;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * Operation that ensures that the contents for remote
+ * of each local resource is cached.
+ */
+public class CacheRemoteContentsOperation extends CacheTreeContentsOperation {
+
+ public CacheRemoteContentsOperation(IWorkbenchPart part, ResourceMapping[] mappers, SyncInfoTree tree) {
+ super(part, mappers, tree);
+ }
+
+ protected boolean needsContents(SyncInfo info) {
+ IResource local = info.getLocal();
+ IResourceVariant remote = info.getRemote();
+ if (remote != null && local.getType() == IResource.FILE) {
+ int direction = SyncInfo.getDirection(info.getKind());
+ if (direction == SyncInfo.CONFLICTING ||
+ direction == SyncInfo.INCOMING) {
+ if (remote instanceof RemoteFile) {
+ RemoteFile remoteFile = (RemoteFile) remote;
+ if (!remoteFile.isContentsCached()) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ protected ICVSRemoteResource buildTree(CVSTeamProvider provider) throws TeamException {
+ return CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber().buildRemoteTree(provider.getProject(), true, new NullProgressMonitor());
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheTreeContentsOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheTreeContentsOperation.java
new file mode 100644
index 000000000..89d0c98ff
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/CacheTreeContentsOperation.java
@@ -0,0 +1,126 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.operations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.synchronize.SyncInfoTree;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.client.*;
+import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * Abstract operation for caching the contents for any files
+ * in a particular remote tree that differ from the local contents.*
+ */
+public abstract class CacheTreeContentsOperation extends SingleCommandOperation {
+
+ private final SyncInfoTree tree;
+
+ public CacheTreeContentsOperation(IWorkbenchPart part, ResourceMapping[] mappers, SyncInfoTree tree) {
+ super(part, mappers, Command.NO_LOCAL_OPTIONS);
+ this.tree = tree;
+ }
+
+ protected void execute(CVSTeamProvider provider, IResource[] resources, boolean recurse, IProgressMonitor monitor) throws CVSException, InterruptedException {
+ IResource[] files = getFilesWithUncachedContents(resources, recurse);
+ if (files.length > 0)
+ super.execute(provider, files, recurse, monitor);
+ }
+
+ private IResource[] getFilesWithUncachedContents(IResource[] resources, boolean recurse) {
+ ArrayList result = new ArrayList();
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ SyncInfo[] infos = tree.getSyncInfos(resource, recurse ? IResource.DEPTH_INFINITE: IResource.DEPTH_ONE);
+ for (int j = 0; j < infos.length; j++) {
+ SyncInfo info = infos[j];
+ if (needsContents(info)) {
+ result.add(info.getLocal());
+ }
+ }
+ }
+ return (IResource[]) result.toArray(new IResource[result.size()]);
+ }
+
+ protected abstract boolean needsContents(SyncInfo info);
+
+ /* (non-Javadoc)
+ *
+ * Use a local root that is really the base tree so we can cache
+ * the base contents without affecting the local contents.
+ *
+ * @see org.eclipse.team.internal.ccvs.ui.operations.RepositoryProviderOperation#getLocalRoot(org.eclipse.team.internal.ccvs.core.CVSTeamProvider)
+ */
+ protected ICVSFolder getLocalRoot(CVSTeamProvider provider)
+ throws CVSException {
+ try {
+ ICVSRemoteResource tree = buildTree(provider);
+ return (ICVSFolder)tree;
+ } catch (TeamException e) {
+ throw CVSException.wrapException(e);
+ }
+ }
+
+ protected abstract ICVSRemoteResource buildTree(CVSTeamProvider provider) throws TeamException;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.operations.RepositoryProviderOperation#getCVSArguments(org.eclipse.core.resources.IResource[])
+ */
+ protected ICVSResource[] getCVSArguments(Session session, IResource[] resources) {
+ List result = new ArrayList();
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ try {
+ ICVSResource file = session.getLocalRoot().getChild(resource.getProjectRelativePath().toString());
+ result.add(file);
+ } catch (CVSException e) {
+ // Log and continue
+ CVSUIPlugin.log(e);
+ }
+ }
+
+ return (ICVSResource[]) result.toArray(new ICVSResource[result.size()]);
+ }
+
+ protected IStatus executeCommand(Session session, CVSTeamProvider provider, ICVSResource[] resources, boolean recurse, IProgressMonitor monitor) throws CVSException, InterruptedException {
+ return Command.UPDATE.execute(
+ session,
+ Command.NO_GLOBAL_OPTIONS,
+ getLocalOptions(true),
+ resources,
+ null,
+ monitor);
+ }
+
+ protected LocalOption[] getLocalOptions(boolean recurse) {
+ return Update.IGNORE_LOCAL_CHANGES.addTo(super.getLocalOptions(recurse));
+ }
+
+ protected String getTaskName(CVSTeamProvider provider) {
+ return NLS.bind("Fetching contents for changed files in {0}", new String[] {provider.getProject().getName()});
+ }
+
+ protected String getTaskName() {
+ return "Fetching contents for changed files";
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/SingleCommandOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/SingleCommandOperation.java
index acb139e3a..731ddee0c 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/SingleCommandOperation.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/SingleCommandOperation.java
@@ -39,7 +39,7 @@ public abstract class SingleCommandOperation extends RepositoryProviderOperation
Session session = new Session(getRemoteLocation(provider), getLocalRoot(provider), true /* output to console */);
session.open(Policy.subMonitorFor(monitor, 10), isServerModificationOperation());
try {
- IStatus status = executeCommand(session, provider, getCVSArguments(resources), recurse, Policy.subMonitorFor(monitor, 90));
+ IStatus status = executeCommand(session, provider, getCVSArguments(session, resources), recurse, Policy.subMonitorFor(monitor, 90));
if (isReportableError(status)) {
throw new CVSException(status);
}
@@ -48,7 +48,15 @@ public abstract class SingleCommandOperation extends RepositoryProviderOperation
}
}
- /* (non-Javadoc)
+ protected final ICVSResource[] getCVSArguments(IResource[] resources) {
+ return super.getCVSArguments(resources);
+ }
+
+ protected ICVSResource[] getCVSArguments(Session session, IResource[] resources) {
+ return getCVSArguments(resources);
+ }
+
+ /* (non-Javadoc)
* @see org.eclipse.team.internal.ccvs.ui.operations.RepositoryProviderOperation#execute(org.eclipse.team.internal.ccvs.core.CVSTeamProvider, org.eclipse.team.internal.ccvs.ui.operations.RepositoryProviderOperation.ICVSTraversal, org.eclipse.core.runtime.IProgressMonitor)
*/
protected void execute(CVSTeamProvider provider, ICVSTraversal entry, IProgressMonitor monitor) throws CVSException, InterruptedException {
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/WorkspaceResourceMapper.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/WorkspaceResourceMapper.java
index 44df6751e..f7593785e 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/WorkspaceResourceMapper.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/operations/WorkspaceResourceMapper.java
@@ -24,6 +24,9 @@ import org.eclipse.core.runtime.IProgressMonitor;
* The resulting mapper will return the workspace root as the model
* object.
*
+ * TODO: The ability to wrap multiple resources in a single mapping
+ * should be provided by the resources plugin.
+ *
* @since 3.1
*/
public final class WorkspaceResourceMapper extends ResourceMapping {
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/RefreshDirtyStateOperation.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/RefreshDirtyStateOperation.java
index f04b7367f..e47739bac 100644
--- a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/RefreshDirtyStateOperation.java
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/subscriber/RefreshDirtyStateOperation.java
@@ -10,29 +10,21 @@
*******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.subscriber;
-import java.util.ArrayList;
-import java.util.List;
+import java.lang.reflect.InvocationTargetException;
import org.eclipse.compare.structuremergeviewer.IDiffElement;
import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.synchronize.*;
import org.eclipse.team.core.synchronize.SyncInfoFilter.ContentComparisonSyncInfoFilter;
-import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.internal.ccvs.core.*;
-import org.eclipse.team.internal.ccvs.core.client.*;
-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.Command.LocalOption;
-import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
-import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
-import org.eclipse.team.internal.ccvs.core.resources.*;
-import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
-import org.eclipse.team.internal.ccvs.core.util.KnownRepositories;
+import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
import org.eclipse.team.internal.ccvs.ui.CVSUIMessages;
import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.operations.CacheBaseContentsOperation;
import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;
/**
@@ -54,7 +46,7 @@ public class RefreshDirtyStateOperation extends CVSSubscriberOperation {
monitor.beginTask(null, 200);
IProject project = infos[0].getLocal().getProject();
ICVSFolder folder = CVSWorkspaceRoot.getCVSFolderFor(project);
- ensureBaseContentsCached(folder, infos, Policy.subMonitorFor(monitor, 100));
+ ensureBaseContentsCached(project, infos, Policy.subMonitorFor(monitor, 100));
folder.run(new ICVSRunnable() {
public void run(IProgressMonitor monitor) throws CVSException {
monitor.beginTask(null, infos.length * 100);
@@ -74,71 +66,15 @@ public class RefreshDirtyStateOperation extends CVSSubscriberOperation {
monitor.done();
}
- private void ensureBaseContentsCached(ICVSFolder project, SyncInfo[] infos, IProgressMonitor monitor) throws CVSException {
- ICVSRepositoryLocation location = getRemoteLocation(project);
- if (location == null) return;
- monitor.beginTask(null, 100);
- SyncInfo[] needContents = getBaseFilesWithUncachedContents(infos, Policy.subMonitorFor(monitor, 10));
- if (needContents.length == 0) return;
- RemoteFolderTree tree = RemoteFolderTreeBuilder.buildBaseTree((CVSRepositoryLocation)location , project, null, Policy.subMonitorFor(monitor, 20));
- ICVSFile[] files = getFilesToUpdate(tree, infos);
- replaceContents(location, tree, files, Policy.subMonitorFor(monitor, 70));
- monitor.done();
- }
-
- private ICVSFile[] getFilesToUpdate(RemoteFolderTree tree, SyncInfo[] infos) throws CVSException {
- List newFiles = new ArrayList();
- for (int i = 0; i < infos.length; i++) {
- SyncInfo info = infos[i];
- ICVSFile file = tree.getFile(info.getLocal().getProjectRelativePath().toString());
- newFiles.add(file);
- }
-
- return (ICVSFile[]) newFiles.toArray(new ICVSFile[newFiles.size()]);
- }
-
- private void replaceContents(ICVSRepositoryLocation location, ICVSFolder project, ICVSFile[] files, IProgressMonitor monitor) throws CVSException {
- monitor.beginTask(null, 100);
- Session session = new Session(location, project, false);
- try {
- session.open(Policy.subMonitorFor(monitor, 10));
- IStatus execute = Command.UPDATE.execute(
- session,
- Command.NO_GLOBAL_OPTIONS,
- new LocalOption[] { Update.IGNORE_LOCAL_CHANGES },
- files,
- null,
- Policy.subMonitorFor(monitor, 90));
- if (execute.getCode() == CVSStatus.SERVER_ERROR) {
- throw new CVSServerException(execute);
- }
- } finally {
- session.close();
- }
-
- }
-
- private SyncInfo[] getBaseFilesWithUncachedContents(SyncInfo[] infos, IProgressMonitor monitor) {
- List files = new ArrayList();
- for (int i = 0; i < infos.length; i++) {
- SyncInfo info = infos[i];
- IResourceVariant base = info.getBase();
- if (base instanceof RemoteFile) {
- RemoteFile remote = (RemoteFile) base;
- if (!remote.isContentsCached()) {
- files.add(info);
- }
- }
- }
- return (SyncInfo[]) files.toArray(new SyncInfo[files.size()]);
- }
-
- private ICVSRepositoryLocation getRemoteLocation(ICVSFolder project) throws CVSException {
- FolderSyncInfo info = project.getFolderSyncInfo();
- if (info == null) {
- return null;
- }
- return KnownRepositories.getInstance().getRepository(info.getRoot());
+ private void ensureBaseContentsCached(IProject project, SyncInfo[] infos, IProgressMonitor monitor) throws CVSException {
+ try {
+ new CacheBaseContentsOperation(getPart(), new ResourceMapping[] { (ResourceMapping)project.getAdapter(ResourceMapping.class) },
+ new SyncInfoTree(infos), true).run(monitor);
+ } catch (InvocationTargetException e) {
+ throw CVSException.wrapException(e);
+ } catch (InterruptedException e) {
+ throw new OperationCanceledException();
+ }
}
protected String getErrorTitle() {
diff --git a/bundles/org.eclipse.team.ui/META-INF/MANIFEST.MF b/bundles/org.eclipse.team.ui/META-INF/MANIFEST.MF
index 78d8969cf..55fb05126 100644
--- a/bundles/org.eclipse.team.ui/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.team.ui/META-INF/MANIFEST.MF
@@ -9,12 +9,14 @@ Bundle-Localization: plugin
Export-Package: org.eclipse.team.internal.ui;x-friends:="org.eclipse.team.cvs.ssh,org.eclipse.team.cvs.ssh2,org.eclipse.team.cvs.ui",
org.eclipse.team.internal.ui.actions;x-friends:="org.eclipse.team.cvs.ssh,org.eclipse.team.cvs.ssh2,org.eclipse.team.cvs.ui",
org.eclipse.team.internal.ui.dialogs;x-friends:="org.eclipse.team.cvs.ssh,org.eclipse.team.cvs.ssh2,org.eclipse.team.cvs.ui",
+ org.eclipse.team.internal.ui.mapping,
org.eclipse.team.internal.ui.preferences;x-friends:="org.eclipse.team.cvs.ssh,org.eclipse.team.cvs.ssh2,org.eclipse.team.cvs.ui",
org.eclipse.team.internal.ui.registry;x-friends:="org.eclipse.team.cvs.ssh,org.eclipse.team.cvs.ssh2,org.eclipse.team.cvs.ui",
org.eclipse.team.internal.ui.synchronize;x-friends:="org.eclipse.team.cvs.ssh,org.eclipse.team.cvs.ssh2,org.eclipse.team.cvs.ui",
org.eclipse.team.internal.ui.synchronize.actions;x-friends:="org.eclipse.team.cvs.ssh,org.eclipse.team.cvs.ssh2,org.eclipse.team.cvs.ui",
org.eclipse.team.internal.ui.wizards;x-friends:="org.eclipse.team.cvs.ssh,org.eclipse.team.cvs.ssh2,org.eclipse.team.cvs.ui",
org.eclipse.team.ui,
+ org.eclipse.team.ui.mapping,
org.eclipse.team.ui.synchronize
Require-Bundle: org.eclipse.ui.ide;resolution:=optional,
org.eclipse.core.resources,
diff --git a/bundles/org.eclipse.team.ui/plugin.xml b/bundles/org.eclipse.team.ui/plugin.xml
index 59c9f6132..bdeb1c8b9 100644
--- a/bundles/org.eclipse.team.ui/plugin.xml
+++ b/bundles/org.eclipse.team.ui/plugin.xml
@@ -314,6 +314,11 @@
class="org.eclipse.team.internal.ui.TeamAdapterFactory">
<adapter type="org.eclipse.ui.model.IWorkbenchAdapter"/>
</factory>
+ <factory
+ adaptableType="org.eclipse.core.resources.mapping.ModelProvider"
+ class="org.eclipse.team.internal.ui.TeamAdapterFactory">
+ <adapter type="org.eclipse.team.internal.ui.mapping.INavigatorContentExtensionFactory"/>
+ </factory>
</extension>
</plugin>
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ResourceMappingContentProvider.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ResourceMappingContentProvider.java
new file mode 100644
index 000000000..1d3deb31c
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ResourceMappingContentProvider.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui;
+
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.team.internal.ui.mapping.IResourceMappingContentProvider;
+import org.eclipse.team.ui.mapping.ITeamViewerContext;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+public class ResourceMappingContentProvider implements IResourceMappingContentProvider {
+
+ /**
+ * TODO Should root be a resource mapping?
+ */
+ final class RootObject implements IWorkbenchAdapter, IAdaptable {
+ final ResourceMapping[] mappings;
+
+ private RootObject(ResourceMapping[] mappings) {
+ super();
+ this.mappings = mappings;
+ }
+
+ public Object[] getChildren(Object o) {
+ return mappings;
+ }
+
+ public ImageDescriptor getImageDescriptor(Object object) {
+ return null;
+ }
+
+ public String getLabel(Object o) {
+ return "Other Elements";
+ }
+
+ public Object getParent(Object o) {
+ return null;
+ }
+
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class)
+ return this;
+ return null;
+ }
+ }
+
+ final RootObject root;
+ private final ITeamViewerContext context;
+ private final String modelId;
+
+ public ResourceMappingContentProvider(ITeamViewerContext context, String modelId) {
+ this.context = context;
+ this.modelId = modelId;
+ root = new RootObject(context.getResourceMappings(modelId));
+ }
+
+ public Object getRoot() {
+ return root;
+ }
+
+ public Object[] getChildren(Object parentElement) {
+ if (parentElement == root)
+ return root.getChildren(parentElement);
+ return new Object[0];
+ }
+
+ public Object getParent(Object element) {
+ if (element == root)
+ return null;
+ ResourceMapping[] mappings = (ResourceMapping[])root.getChildren(root);
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ if (element == mapping)
+ return root;
+ }
+ return null;
+ }
+
+ public boolean hasChildren(Object element) {
+ if (element == root)
+ return true;
+ return false;
+ }
+
+ public Object[] getElements(Object inputElement) {
+ return getChildren(inputElement);
+ }
+
+ public void dispose() {
+ // Nothing to do
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ // Nothing to do
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ResourceNavigatorContentExtensionFactory.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ResourceNavigatorContentExtensionFactory.java
new file mode 100644
index 000000000..cbccb34cc
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/ResourceNavigatorContentExtensionFactory.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui;
+
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.team.internal.ui.dialogs.ResourceMappingLabelProvider;
+import org.eclipse.team.internal.ui.mapping.*;
+import org.eclipse.team.ui.mapping.*;
+
+public class ResourceNavigatorContentExtensionFactory implements
+ INavigatorContentExtensionFactory {
+
+ public NavigatorContentExtension createProvider(ITeamViewerContext context) {
+ return new NavigatorContentExtension(context) {
+ private ResourceMappingContentProvider resourceMappingContentProvider;
+ public IResourceMappingContentProvider getContentProvider() {
+ if (resourceMappingContentProvider == null)
+ resourceMappingContentProvider = new ResourceMappingContentProvider(getContext(), ModelProvider.RESOURCE_MODEL_PROVIDER_ID);
+ return resourceMappingContentProvider;
+ }
+ public void dispose() {
+ resourceMappingContentProvider.dispose();
+ super.dispose();
+ }
+ };
+ }
+
+ public ILabelProvider getLabelProvider() {
+ return new ResourceMappingLabelProvider();
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamAdapterFactory.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamAdapterFactory.java
index 59fb42af7..f7b8a1976 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamAdapterFactory.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/TeamAdapterFactory.java
@@ -11,7 +11,9 @@
package org.eclipse.team.internal.ui;
import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.core.resources.mapping.ModelProvider;
import org.eclipse.core.runtime.IAdapterFactory;
+import org.eclipse.team.internal.ui.mapping.INavigatorContentExtensionFactory;
import org.eclipse.team.internal.ui.synchronize.DiffNodeWorkbenchAdapter;
import org.eclipse.ui.model.IWorkbenchAdapter;
@@ -27,6 +29,9 @@ public class TeamAdapterFactory implements IAdapterFactory {
if(adaptableObject instanceof DiffNode && adapterType == IWorkbenchAdapter.class) {
return diffNodeAdapter;
}
+ if (adaptableObject instanceof ModelProvider && adapterType == INavigatorContentExtensionFactory.class) {
+ return new ResourceNavigatorContentExtensionFactory();
+ }
return null;
}
@@ -35,6 +40,6 @@ public class TeamAdapterFactory implements IAdapterFactory {
*/
public Class[] getAdapterList() {
// TODO Auto-generated method stub
- return new Class[] {IWorkbenchAdapter.class};
+ return new Class[] {IWorkbenchAdapter.class, INavigatorContentExtensionFactory.class};
}
}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/AdditionalMappingsDialog.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/AdditionalMappingsDialog.java
new file mode 100644
index 000000000..a0743379b
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/AdditionalMappingsDialog.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.dialogs;
+
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.team.ui.mapping.ITeamViewerContext;
+
+public class AdditionalMappingsDialog extends DetailsDialog {
+
+ private final ResourceMapping[] selectedMappings;
+ private ResourceMappingSelectionArea selectedMappingsArea;
+ private ResourceMappingHierarchyArea allMappingsArea;
+ private ITeamViewerContext context;
+
+ public AdditionalMappingsDialog(Shell parentShell, String dialogTitle, ResourceMapping[] selectedMappings, ITeamViewerContext context) {
+ super(parentShell, dialogTitle);
+ this.selectedMappings = selectedMappings;
+ this.context = context;
+ }
+
+ protected void createMainDialogArea(Composite parent) {
+ createWrappingLabel(parent, "This dialog shows all the selected elements and all the elements that will be operated on by this operation due to the files in which the elements are stored.");
+ createSelectedMappingsArea(parent);
+ createAllMappingsArea(parent);
+ }
+
+ /*
+ * Create a list that allows the selection of mappings via checkbox
+ */
+ private void createSelectedMappingsArea(Composite parent) {
+ Composite composite = createComposite(parent);
+ selectedMappingsArea = new ResourceMappingSelectionArea(selectedMappings, false, false);
+ selectedMappingsArea.setDescription("Selected Elements");
+ //selectedMappingsArea.addPropertyChangeListener(this);
+ selectedMappingsArea.createArea(composite);
+ // Create a separator between the two sets of buttons
+ Label seperator = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
+ seperator.setLayoutData(new GridData (GridData.FILL_HORIZONTAL));
+ }
+
+ /*
+ * Create a list that allows the selection of mappings via checkbox
+ */
+ private void createAllMappingsArea(Composite parent) {
+ Composite composite = createComposite(parent);
+ allMappingsArea = ResourceMappingHierarchyArea.create(context);
+ allMappingsArea.setDescription("All elements to be operated on");
+ //allMappingsArea.addPropertyChangeListener(this);
+ allMappingsArea.createArea(composite);
+ // Create a separator between the two sets of buttons
+ Label seperator = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL);
+ seperator.setLayoutData(new GridData (GridData.FILL_HORIZONTAL));
+ }
+
+ protected Composite createDropDownDialogArea(Composite parent) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ protected void updateEnablements() {
+ // TODO Auto-generated method stub
+
+ }
+
+ protected boolean includeDetailsButton() {
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/MappingSelectionDialog.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/MappingSelectionDialog.java
index 2b46d7419..ef4ad93ac 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/MappingSelectionDialog.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/MappingSelectionDialog.java
@@ -54,7 +54,7 @@ public abstract class MappingSelectionDialog extends DetailsDialog implements IP
*/
private void createMappingSelectionArea(Composite parent) {
Composite composite = createComposite(parent);
- mappingArea = new ResourceMappingSelectionArea(mappings);
+ mappingArea = new ResourceMappingSelectionArea(mappings, true, true);
mappingArea.setDescription(getMultipleMappingsMessage());
mappingArea.addPropertyChangeListener(this);
mappingArea.createArea(composite);
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingHierarchyArea.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingHierarchyArea.java
new file mode 100644
index 000000000..be39a4ccc
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingHierarchyArea.java
@@ -0,0 +1,278 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.dialogs;
+
+import java.util.*;
+
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.team.internal.ui.TeamUIPlugin;
+import org.eclipse.team.internal.ui.mapping.*;
+import org.eclipse.team.ui.mapping.*;
+
+public class ResourceMappingHierarchyArea extends DialogArea {
+
+ private String description;
+ private TreeViewer viewer;
+ private final CompositeContentProvider contentProvider;
+
+ /*
+ * TODO: There are some potential problems here
+ * - the input changed method probably should not be propagated to the
+ * sub-providers. Perhaps an additional method is needed (setViewer)?
+ * - this content provider has state that is dependent on what is
+ * displayed in the view. Should a refresh of the viewer clear this state?
+ * I don't think it needs to unless the input changes (which it never does
+ * after the first set).
+ */
+ private static class CompositeContentProvider implements IResourceMappingContentProvider, ILabelProvider {
+
+ private final Map providers; // Map of ModelProvider -> NavigatorContentExtension
+ private final Map providerMap = new HashMap();
+ private final ILabelProvider defaultLabelProvider = new ResourceMappingLabelProvider();
+
+ public CompositeContentProvider(Map providers) {
+ this.providers = providers;
+ }
+
+ public Object getRoot() {
+ IResourceMappingContentProvider provider = getSingleProvider();
+ if (provider != null) {
+ Object root = provider.getRoot();
+ providerMap.put(root, getNavigatorContentExtension(root));
+ return root;
+ }
+ return this;
+ }
+
+ public Object[] getChildren(Object parentElement) {
+ IResourceMappingContentProvider singleProvider = getSingleProvider();
+ if (singleProvider != null) {
+ return singleProvider.getChildren(parentElement);
+ }
+ if (parentElement == this) {
+ List result = new ArrayList();
+ for (Iterator iter = providers.values().iterator(); iter.hasNext();) {
+ NavigatorContentExtension extension = (NavigatorContentExtension) iter.next();
+ IResourceMappingContentProvider provider = extension.getContentProvider();
+ Object element = provider.getRoot();
+ providerMap.put(element, extension);
+ result.add(element);
+ }
+ return result.toArray(new Object[result.size()]);
+ } else {
+ NavigatorContentExtension extension = getNavigatorContentExtension(parentElement);
+ if (extension != null) {
+ Object[] elements = extension.getContentProvider().getChildren(parentElement);
+ for (int i = 0; i < elements.length; i++) {
+ Object element = elements[i];
+ providerMap.put(element, extension);
+ }
+ return elements;
+ }
+ }
+ return new Object[0];
+ }
+
+ public Object getParent(Object element) {
+ IResourceMappingContentProvider singleProvider = getSingleProvider();
+ if (singleProvider != null) {
+ return singleProvider.getParent(element);
+ }
+ if (element == this)
+ return null;
+ IResourceMappingContentProvider provider = getProvider(element);
+ if (element == provider.getRoot()) {
+ return this;
+ }
+ return provider.getParent(element);
+ }
+
+ private NavigatorContentExtension getNavigatorContentExtension(Object element) {
+ if (providers.size() == 1) {
+ return ((NavigatorContentExtension)providers.values().iterator().next());
+ }
+ return (NavigatorContentExtension)providerMap.get(element);
+ }
+
+ private IResourceMappingContentProvider getSingleProvider() {
+ if (providers.size() == 1)
+ return getNavigatorContentExtension(this).getContentProvider();
+ return null;
+ }
+
+ private IResourceMappingContentProvider getProvider(Object element) {
+ NavigatorContentExtension e = getNavigatorContentExtension(element);
+ if (e == null)
+ return null;
+ return e.getContentProvider();
+ }
+
+ public boolean hasChildren(Object element) {
+ IResourceMappingContentProvider singleProvider = getSingleProvider();
+ if (singleProvider != null) {
+ return singleProvider.hasChildren(element);
+ }
+ if (element != this) {
+ IResourceMappingContentProvider provider = getProvider(element);
+ if (provider != null)
+ return provider.hasChildren(element);
+ }
+ return getChildren(element).length > 0;
+ }
+
+ public Object[] getElements(Object inputElement) {
+ IResourceMappingContentProvider singleProvider = getSingleProvider();
+ if (singleProvider != null) {
+ return singleProvider.getElements(inputElement);
+ }
+ if (inputElement != this) {
+ IResourceMappingContentProvider provider = getProvider(inputElement);
+ if (provider != null)
+ return provider.getElements(inputElement);
+ }
+ return getChildren(inputElement);
+ }
+
+ public void dispose() {
+ providerMap.clear();
+ for (Iterator iter = providers.values().iterator(); iter.hasNext();) {
+ NavigatorContentExtension extension = (NavigatorContentExtension) iter.next();
+ extension.dispose();
+ }
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ providerMap.clear();
+ for (Iterator iter = providers.values().iterator(); iter.hasNext();) {
+ NavigatorContentExtension extension = (NavigatorContentExtension) iter.next();
+ IResourceMappingContentProvider provider = extension.getContentProvider();
+ provider.inputChanged(viewer, oldInput, newInput);
+ }
+ }
+
+ private ILabelProvider getLabelProvider(Object o) {
+ if (o != this) {
+ NavigatorContentExtension e = getNavigatorContentExtension(o);
+ if (e != null)
+ return e.getLabelProvider();
+ Object parent = getParent(o);
+ if (parent != null)
+ return getLabelProvider(parent);
+ }
+ return defaultLabelProvider;
+ }
+
+ public Image getImage(Object element) {
+ return getLabelProvider(element).getImage(element);
+ }
+
+ public String getText(Object element) {
+ return getLabelProvider(element).getText(element);
+ }
+
+ public void addListener(ILabelProviderListener listener) {
+ defaultLabelProvider.addListener(listener);
+ for (Iterator iter = providers.values().iterator(); iter.hasNext();) {
+ NavigatorContentExtension extension = (NavigatorContentExtension) iter.next();
+ ILabelProvider lp = extension.getLabelProvider();
+ lp.addListener(listener);
+ }
+ }
+
+ public boolean isLabelProperty(Object element, String property) {
+ return getLabelProvider(element).isLabelProperty(element, property);
+ }
+
+ public void removeListener(ILabelProviderListener listener) {
+ defaultLabelProvider.removeListener(listener);
+ for (Iterator iter = providers.values().iterator(); iter.hasNext();) {
+ NavigatorContentExtension extension = (NavigatorContentExtension) iter.next();
+ ILabelProvider lp = extension.getLabelProvider();
+ lp.removeListener(listener);
+ }
+ }
+
+ }
+
+ public static ResourceMappingHierarchyArea create(ITeamViewerContext context) {
+ ModelProvider[] providers = context.getModelProviders();
+ Map extensions = new HashMap();
+ for (int i = 0; i < providers.length; i++) {
+ ModelProvider provider = providers[i];
+ INavigatorContentExtensionFactory factory = getFactory(provider);
+ if (factory == null) {
+ try {
+ ModelProvider resourceModelProvider = ModelProvider.getModelProviderDescriptor(ModelProvider.RESOURCE_MODEL_PROVIDER_ID).getModelProvider();
+ if (!extensions.containsKey(resourceModelProvider)) {
+ factory = getFactory(resourceModelProvider);
+ }
+ } catch (CoreException e) {
+ TeamUIPlugin.log(e);
+ }
+ }
+ if (factory != null) {
+ NavigatorContentExtension extension = factory.createProvider(context);
+ extensions.put(provider, extension);
+ }
+ }
+ CompositeContentProvider provider = new CompositeContentProvider(extensions);
+ return new ResourceMappingHierarchyArea(provider);
+ }
+
+ private static INavigatorContentExtensionFactory getFactory(ModelProvider provider) {
+ return (INavigatorContentExtensionFactory) provider.getAdapter(INavigatorContentExtensionFactory.class);
+ }
+
+ private ResourceMappingHierarchyArea(CompositeContentProvider contentProvider) {
+ this.contentProvider = contentProvider;
+ }
+
+ public void createArea(Composite parent) {
+ Composite composite = createComposite(parent, 1, true);
+ GridLayout layout = new GridLayout(1, false);
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ layout.verticalSpacing = 0;
+ layout.horizontalSpacing = 0;
+ composite.setLayout(layout);
+
+ if (description != null)
+ createWrappingLabel(composite, description, 1);
+
+ viewer = new TreeViewer(composite);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ data.heightHint = 100;
+ data.widthHint = 300;
+ viewer.getControl().setLayoutData(data);
+ viewer.setContentProvider(getContentProvider());
+ viewer.setLabelProvider(getContentProvider());
+ viewer.setInput(getInput());
+ }
+
+ private Object getInput() {
+ return getContentProvider().getRoot();
+ }
+
+ private CompositeContentProvider getContentProvider() {
+ return contentProvider;
+ }
+
+ public void setDescription(String string) {
+ description = string;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingLabelProvider.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingLabelProvider.java
new file mode 100644
index 000000000..574020c4b
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingLabelProvider.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.dialogs;
+
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+
+public class ResourceMappingLabelProvider extends LabelProvider {
+ WorkbenchLabelProvider provider = new WorkbenchLabelProvider();
+ public String getText(Object element) {
+ String text = provider.getText(element);
+ if (text != null && text.length() > 0)
+ return text;
+ if (element instanceof ResourceMapping) {
+ ResourceMapping mapping = (ResourceMapping) element;
+ text = provider.getText(mapping.getModelObject());
+ if (text != null && text.length() > 0)
+ return text;
+ }
+ return super.getText(element);
+ }
+ public Image getImage(Object element) {
+ Image image = provider.getImage(element);
+ if (image != null)
+ return image;
+ if (element instanceof ResourceMapping) {
+ ResourceMapping mapping = (ResourceMapping) element;
+ image = provider.getImage(mapping.getModelObject());
+ if (image != null)
+ return image;
+ }
+ return super.getImage(element);
+ }
+ public void dispose() {
+ provider.dispose();
+ super.dispose();
+ }
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingResourceDisplayArea.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingResourceDisplayArea.java
index 755fda92d..09e0b9d1a 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingResourceDisplayArea.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingResourceDisplayArea.java
@@ -11,7 +11,10 @@
package org.eclipse.team.internal.ui.dialogs;
import java.lang.reflect.InvocationTargetException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.mapping.*;
@@ -19,6 +22,7 @@ import org.eclipse.core.runtime.*;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.layout.GridData;
@@ -26,8 +30,11 @@ import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.team.internal.core.Policy;
import org.eclipse.team.internal.ui.TeamUIPlugin;
+import org.eclipse.team.internal.ui.mapping.INavigatorContentExtensionFactory;
import org.eclipse.ui.PlatformUI;
-import org.eclipse.ui.model.*;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.views.navigator.ResourceSorter;
/**
@@ -55,7 +62,16 @@ public class ResourceMappingResourceDisplayArea extends DialogArea {
* @return it's label
*/
public static String getLabel(ResourceMapping mapping) {
- Object o = mapping;
+ Object o = null;
+ o = mapping.getModelProvider().getAdapter(INavigatorContentExtensionFactory.class);
+ if (o instanceof INavigatorContentExtensionFactory) {
+ INavigatorContentExtensionFactory factory = (INavigatorContentExtensionFactory) o;
+ ILabelProvider labelProvider = factory.getLabelProvider();
+ String text = labelProvider.getText(mapping);
+ labelProvider.dispose(); // TODO should keep label provider around
+ return text;
+ }
+ o = mapping;
IWorkbenchAdapter workbenchAdapter = getWorkbenchAdapter((IAdaptable)o);
if (workbenchAdapter == null) {
Object modelObject = mapping.getModelObject();
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingSelectionArea.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingSelectionArea.java
index 72fe6cd41..64d136fc4 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingSelectionArea.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/dialogs/ResourceMappingSelectionArea.java
@@ -15,52 +15,21 @@ import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
-import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Table;
import org.eclipse.team.internal.ui.TeamUIMessages;
-import org.eclipse.ui.model.*;
+import org.eclipse.ui.model.AdaptableList;
+import org.eclipse.ui.model.BaseWorkbenchContentProvider;
/**
* Dialog area that displays a checkbox list of mappings.
*/
public class ResourceMappingSelectionArea extends DialogArea {
- public class ResourceMappingLabelProvider extends LabelProvider {
- WorkbenchLabelProvider provider = new WorkbenchLabelProvider();
- public String getText(Object element) {
- String text = provider.getText(element);
- if (text != null && text.length() > 0)
- return text;
- if (element instanceof ResourceMapping) {
- ResourceMapping mapping = (ResourceMapping) element;
- text = provider.getText(mapping.getModelObject());
- if (text != null)
- return text;
- }
- return super.getText(element);
- }
- public Image getImage(Object element) {
- Image image = provider.getImage(element);
- if (image != null)
- return image;
- if (element instanceof ResourceMapping) {
- ResourceMapping mapping = (ResourceMapping) element;
- image = provider.getImage(mapping.getModelObject());
- if (image != null)
- return image;
- }
- return super.getImage(element);
- }
- public void dispose() {
- provider.dispose();
- super.dispose();
- }
- }
-
- /**
+ /**
* Property constant used to indicate that the selected mapping has changed.
* The object associated with the property is a <code>ResourceMapping</code>.
*/
@@ -74,13 +43,17 @@ public class ResourceMappingSelectionArea extends DialogArea {
public static final String CHECKED_MAPPINGS = "CheckedMappings"; //$NON-NLS-1$
private ResourceMapping[] mappings;
- private CheckboxTableViewer viewer;
+ private TableViewer viewer;
private ResourceMapping[] checkedMappings;
private ResourceMapping selectedMapping;
private String description;
+ private boolean supportsChecking;
+ private boolean supportsSelection;
- public ResourceMappingSelectionArea(ResourceMapping[] mappings) {
+ public ResourceMappingSelectionArea(ResourceMapping[] mappings, boolean supportSelection, boolean supportChecking) {
this.mappings = mappings;
+ this.supportsChecking = supportChecking;
+ this.supportsSelection = supportSelection;
}
/* (non-Javadoc)
@@ -98,7 +71,7 @@ public class ResourceMappingSelectionArea extends DialogArea {
if (description != null)
createWrappingLabel(composite, description, 1);
- viewer = CheckboxTableViewer.newCheckList(composite, SWT.SINGLE | SWT.BORDER);
+ createViewer(composite);
GridData data = new GridData(GridData.FILL_BOTH);
data.heightHint = 100;
data.widthHint = 300;
@@ -106,23 +79,31 @@ public class ResourceMappingSelectionArea extends DialogArea {
viewer.setContentProvider(new BaseWorkbenchContentProvider());
viewer.setLabelProvider(new ResourceMappingLabelProvider());
viewer.setInput(new AdaptableList(mappings));
- viewer.addCheckStateListener(new ICheckStateListener() {
- public void checkStateChanged(CheckStateChangedEvent event) {
- ResourceMapping[] oldMappings = checkedMappings;
- checkedMappings = internalGetCheckedMappings();
- if (oldMappings != checkedMappings)
- firePropertyChangeChange(CHECKED_MAPPINGS, oldMappings, checkedMappings);
- }
- });
- viewer.addSelectionChangedListener(new ISelectionChangedListener() {
- public void selectionChanged(SelectionChangedEvent event) {
- ResourceMapping oldSelection = selectedMapping;
- selectedMapping = internalGetSelectedMapping();
- if (oldSelection != selectedMapping)
- firePropertyChangeChange(SELECTED_MAPPING, oldSelection, selectedMapping);
- }
+ if (isSupportsSelection()) {
+ viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ ResourceMapping oldSelection = selectedMapping;
+ selectedMapping = internalGetSelectedMapping();
+ if (oldSelection != selectedMapping)
+ firePropertyChangeChange(SELECTED_MAPPING, oldSelection, selectedMapping);
+ }
+ });
+ }
+ if (isSupportsChecking())
+ initializeCheckboxViewer(composite);
+ }
+
+ private void initializeCheckboxViewer(Composite composite) {
+ final CheckboxTableViewer checkboxViewer = getCheckboxTableViewer();
+ checkboxViewer.addCheckStateListener(new ICheckStateListener() {
+ public void checkStateChanged(CheckStateChangedEvent event) {
+ ResourceMapping[] oldMappings = checkedMappings;
+ checkedMappings = internalGetCheckedMappings();
+ if (oldMappings != checkedMappings)
+ firePropertyChangeChange(CHECKED_MAPPINGS, oldMappings, checkedMappings);
+ }
});
- viewer.setCheckedElements(mappings);
+ checkboxViewer.setCheckedElements(mappings);
checkedMappings = mappings;
Composite buttons = createEmbeddedButtonComposite(composite);
@@ -132,7 +113,7 @@ public class ResourceMappingSelectionArea extends DialogArea {
selectAll.setLayoutData(new GridData(GridData.FILL_BOTH));
selectAll.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
- viewer.setAllChecked(true);
+ checkboxViewer.setAllChecked(true);
}
});
@@ -141,13 +122,27 @@ public class ResourceMappingSelectionArea extends DialogArea {
deselectAll.setLayoutData(new GridData(GridData.FILL_BOTH));
deselectAll.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
- viewer.setAllChecked(false);
+ checkboxViewer.setAllChecked(false);
}
});
- }
+ }
+
+ private void createViewer(Composite composite) {
+ if (isSupportsChecking())
+ viewer = CheckboxTableViewer.newCheckList(composite, getViewerStyle());
+ else
+ viewer = new TableViewer(new Table(composite, getViewerStyle()));
+ }
+
+ private int getViewerStyle() {
+ int style = SWT.BORDER;
+ if (isSupportsSelection())
+ style |= SWT.SINGLE;
+ return style;
+ }
/* private */ ResourceMapping[] internalGetCheckedMappings() {
- Object[] checked = viewer.getCheckedElements();
+ Object[] checked = getCheckboxTableViewer().getCheckedElements();
ResourceMapping[] mappings = new ResourceMapping[checked.length];
for (int i = 0; i < checked.length; i++) {
Object object = checked[i];
@@ -190,4 +185,16 @@ public class ResourceMappingSelectionArea extends DialogArea {
public ResourceMapping getSelectedMapping() {
return selectedMapping;
}
+
+ private CheckboxTableViewer getCheckboxTableViewer() {
+ return (CheckboxTableViewer)viewer;
+ }
+
+ public boolean isSupportsChecking() {
+ return supportsChecking;
+ }
+
+ public boolean isSupportsSelection() {
+ return supportsSelection;
+ }
}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/DefaultResourceMappingMerger.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/DefaultResourceMappingMerger.java
new file mode 100644
index 000000000..118656d98
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/DefaultResourceMappingMerger.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.synchronize.SyncInfoTree;
+import org.eclipse.team.internal.ui.Policy;
+import org.eclipse.team.ui.mapping.IMergeContext;
+import org.eclipse.team.ui.mapping.IResourceMappingMerger;
+import org.eclipse.team.ui.mapping.IResourceMappingOperationInput;
+import org.eclipse.team.ui.mapping.MergeStatus;
+
+/**
+ * A default merger that delegates the merge to the merge context.
+ */
+public class DefaultResourceMappingMerger implements IResourceMappingMerger {
+
+ private final ModelProvider provider;
+ private final IResourceMappingOperationInput input;
+
+ public DefaultResourceMappingMerger(ModelProvider provider, IResourceMappingOperationInput input) {
+ this.provider = provider;
+ this.input = input;
+ }
+
+ public IStatus merge(IMergeContext mergeContext, IProgressMonitor monitor) throws CoreException {
+ try {
+ SyncInfoTree tree = getSetToMerge(mergeContext);
+ monitor.beginTask(null, 100);
+ IStatus status = mergeContext.merge(tree, Policy.subMonitorFor(monitor, 75));
+ return covertFilesToMappings(status, mergeContext);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ private SyncInfoTree getSetToMerge(IMergeContext mergeContext) {
+ ResourceMapping[] mappings = input.getMappings(provider);
+ SyncInfoTree result = new SyncInfoTree();
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ ResourceTraversal[] traversals = input.getTraversal(mapping);
+ SyncInfo[] infos = mergeContext.getSyncInfoTree().getSyncInfos(traversals);
+ for (int j = 0; j < infos.length; j++) {
+ SyncInfo info = infos[j];
+ result.add(info);
+ }
+ }
+ return result;
+ }
+
+ private IStatus covertFilesToMappings(IStatus status, IMergeContext mergeContext) {
+ if (status.getCode() == MergeStatus.CONFLICTS) {
+ // In general, we can't say which mapping failed so return them all
+ return new MergeStatus(status.getPlugin(), status.getMessage(), input.getMappings(provider));
+ }
+ return status;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/INavigatorContentExtensionFactory.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/INavigatorContentExtensionFactory.java
new file mode 100644
index 000000000..2bdcdb029
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/INavigatorContentExtensionFactory.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.team.ui.mapping.ITeamViewerContext;
+
+/**
+ * Factory for creating a NavigatorContentProvider for
+ * a given team context.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface INavigatorContentExtensionFactory {
+
+ public NavigatorContentExtension createProvider(ITeamViewerContext context);
+
+ /**
+ * TODO: Should not need this but I added it to make it work
+ */
+ public ILabelProvider getLabelProvider();
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/IResourceMappingContentProvider.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/IResourceMappingContentProvider.java
new file mode 100644
index 000000000..4fa020db3
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/IResourceMappingContentProvider.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import org.eclipse.jface.viewers.ITreeContentProvider;
+
+/**
+ * A model content provider is a tree content provider that is used to display
+ * specific elements of a logical model. The only additional attribute of a model
+ * content provider is that it also provides the root of the model being displayed.
+ * This root may be used as the viewer input (in cases where the viewer is only displaying
+ * a single model) or may be used as one of multiple elements appearing at the top
+ * level in the viewer (in cases where the viewer contains multiple models).
+ */
+public interface IResourceMappingContentProvider extends ITreeContentProvider {
+
+ /**
+ * Returns the root element of the model tree being displayed.
+ * This element may or may not appear in the viewer depending on
+ * whether the viewer is displaying a single logical model (in which case
+ * the root need not be displayed) or multiple logical models (in which
+ * case the root needs to be displayed to separate the mappings of different
+ * models).
+ * @return the root element of the model tree being displayed.
+ */
+ public Object getRoot();
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/NavigatorContentExtension.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/NavigatorContentExtension.java
new file mode 100644
index 000000000..062389c23
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/NavigatorContentExtension.java
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import org.eclipse.jface.viewers.ILabelProvider;
+import org.eclipse.team.internal.ui.dialogs.ResourceMappingLabelProvider;
+import org.eclipse.team.ui.mapping.ITeamViewerContext;
+
+/**
+ * Placeholder for Common Navigator extension
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * since 3.2
+ *
+ */
+public abstract class NavigatorContentExtension {
+
+ private static final ResourceMappingLabelProvider RESOURCE_MAPPING_LABEL_PROVIDER = new ResourceMappingLabelProvider();
+ private final ITeamViewerContext context;
+
+ public NavigatorContentExtension(ITeamViewerContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Returns a content provider that can be used to display the provided
+ * mappings in an element hierarchy that is consistent with the model. The
+ * resulting hierarchy may contain additional elements as long as they are
+ * for organizational purposes only. Any additional elements must not
+ * include additional resources (i.e. <code>IResource</code>) in the
+ * resource mappings contained in the view.
+ *
+ * @return a content provider that will arrange the provided mappings into a
+ * hierarchy.
+ */
+ public abstract IResourceMappingContentProvider getContentProvider();
+
+ /**
+ * Return a label provider that can be used to provide labels for any
+ * resource mappings that adapt to this factory and any model objects that
+ * appear in the tree created by the
+ * <code>IResourceMappingContentProvider</code> returned from the
+ * <code>createContentProvider</code>. method.
+ *
+ * @return a label provider
+ */
+ public ILabelProvider getLabelProvider() {
+ return RESOURCE_MAPPING_LABEL_PROVIDER;
+ }
+
+ public ITeamViewerContext getContext() {
+ return context;
+ }
+
+ public void dispose() {
+ // TODO Auto-generated method stub
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingCheckinOperation.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingCheckinOperation.java
new file mode 100644
index 000000000..a07cc4f2d
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingCheckinOperation.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.ui.mapping.IResourceMappingOperationInput;
+import org.eclipse.team.ui.mapping.ResourceMappingOperation;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * The check-in operation needs to delegate the check-in to the
+ * repository tooling somehow. The things we can do up front is to
+ * adjust the input using the participants. TO do this, we need the
+ * resource mapping context of the check-in.
+ * <p>
+ * Another thing we could potentially do is display a model diff
+ * tree that shows the outgoing changes. This would need to be
+ * integrated with the repository tooling check-in UI artifacts
+ * (e.g. commit comment).
+ *
+ * The steps of a check-in operation are:
+ * <ol>
+ * <li>Obtain the selection to be operated on.
+ * <li>Determine the projection of the selection onto resources
+ * using resource mappings and traversals.
+ * <ul>
+ * <li>this will require traversals from ancestor
+ * <li>may require traversals from remote for conflict notification
+ * </ul>
+ * <li>Ensure that all affected mappings are known
+ * <ul>
+ * <li>additional mappings may be included due to resource project
+ * (i.e. many-to-one case).
+ * <li>notify users of additional mappings that will be affected.
+ * <li>this list must include locally modified model elements including
+ * deletions.
+ * <li>the list could also indicate remote changes that will cause the commit
+ * to fail
+ * </ul>
+ * <li>Prompt the user for additional information required by check-in
+ * (e.g. comment)
+ * <ul>
+ * <li>This may show model elements being included in the commit
+ * <li>Could also show sync state and support merging
+ * <li>Could if support exclusion of model elements? This would be complicated.
+ * </ul>
+ * <li>Perform the check-in on the resources
+ * </ol>
+ * <p>
+ * Special case involving sub-file elements.
+ * <ul>
+ * <li>Level 1: prompt to indicate that the check-in will include
+ * additional elements (Need a way to detect this is the case).
+ * <li>Level 2: prompt to display the additional elements. A participant
+ * can provide the elements but they need to be able to include deletions.
+ * Display could be a flat list that makes use of Generic Navigator type API.
+ * <li>Level 3: Display diff tree and highlight the additional elements.
+ * Need to be able to identify and highlight the additional elements.
+ * </ul>
+ * One way to handle this would be to delegate the prompt to the model provider.
+ * That is, collect the resource mappings involved and ask the model provider
+ * to indicate to the user what additional resource mappings will be operated
+ * on and return an adjusted list.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class ResourceMappingCheckinOperation extends ResourceMappingOperation {
+
+ protected ResourceMappingCheckinOperation(IWorkbenchPart part, IResourceMappingOperationInput input) {
+ super(part, input);
+ }
+
+ protected void execute(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingLoadOperation.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingLoadOperation.java
new file mode 100644
index 000000000..cbbe86858
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingLoadOperation.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.ui.mapping.IResourceMappingOperationInput;
+import org.eclipse.team.ui.mapping.ResourceMappingOperation;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * The steps of an load (replace with version or branch) operation are:
+ * <ol>
+ * <li>Obtain the selection to be operated on.
+ * <li>Determine the projection of the selection onto resources
+ * using resource mappings and traversals.
+ * <ul>
+ * <li>this will require traversal of remote only
+ * </ul>
+ * <li>Ensure that all affected mappings are known
+ * <ul>
+ * <li>additional mappings may be included due to resource project
+ * (i.e. many-to-one case).
+ * <li>notify users of additional mappings that will be affected
+ * <li>this list must include locally changed model elements whose
+ * changes will be lost including any that were deleted.
+ * <li>this list could include changed remote elements that
+ * will be received including additions
+ * </ul>
+ * <li>Perform the replace at the resource level
+ * </ol>
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 32
+ */
+public class ResourceMappingLoadOperation extends ResourceMappingOperation {
+
+ protected ResourceMappingLoadOperation(IWorkbenchPart part, IResourceMappingOperationInput input) {
+ super(part, input);
+ }
+
+ protected void execute(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingScope.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingScope.java
new file mode 100644
index 000000000..41f4b41e3
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/ResourceMappingScope.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
+import org.eclipse.team.ui.synchronize.AbstractSynchronizeScope;
+
+/**
+ * A synchronize scope whose roots are defined by the traversals
+ * obtained from a set of resource mappings.
+ * @since 3.2
+ */
+public class ResourceMappingScope extends AbstractSynchronizeScope {
+
+ private ResourceMapping[] mappings;
+ private ResourceTraversal[] traversals;
+ private String name;
+ private IResource[] roots;
+
+ /**
+ * Create a resource mapping scope.
+ * @param name the name used to describe the scope (this may be displayed to users)
+ * @param mappings the mappings that define the scope
+ * @param traversals the traversals derived from the mappings using the context of the operation being performed
+ */
+ public ResourceMappingScope(String name, ResourceMapping[] mappings, ResourceTraversal[] traversals) {
+ this.name = name;
+ this.mappings = mappings;
+ this.traversals = traversals;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.ISynchronizeScope#getName()
+ */
+ public String getName() {
+ return name;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.ISynchronizeScope#getRoots()
+ */
+ public IResource[] getRoots() {
+ if (roots == null) {
+ Set result = new HashSet();
+ for (int i = 0; i < traversals.length; i++) {
+ ResourceTraversal traversal = traversals[i];
+ IResource[] resources = traversal.getResources();
+ for (int j = 0; j < resources.length; j++) {
+ IResource resource = resources[j];
+ //TODO: should we check for parent/child relationships?
+ result.add(resource);
+ }
+ }
+ roots = (IResource[]) result.toArray(new IResource[result.size()]);
+ }
+ return roots;
+ }
+
+ /**
+ * Return the resource mappings used to define this scope.
+ * @return the resource mappings used to define this scope
+ */
+ public ResourceMapping[] getResourceMappings() {
+ return mappings;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.ISynchronizeScope#contains(org.eclipse.core.resources.IResource)
+ */
+ public boolean contains(IResource resource) {
+ for (int i = 0; i < traversals.length; i++) {
+ ResourceTraversal traversal = traversals[i];
+ if (traversal.contains(resource)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/SimpleResourceMappingOperationInput.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/SimpleResourceMappingOperationInput.java
new file mode 100644
index 000000000..feb958b57
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/SimpleResourceMappingOperationInput.java
@@ -0,0 +1,152 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import java.util.*;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ui.Policy;
+import org.eclipse.team.ui.mapping.IResourceMappingOperationInput;
+import org.eclipse.team.ui.synchronize.ISynchronizeScope;
+
+/**
+ * A simple implementation of an operation input that
+ * does not transform the input.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class SimpleResourceMappingOperationInput implements
+ IResourceMappingOperationInput {
+
+ private ResourceMapping[] mappings;
+ private ResourceMappingContext context;
+ private Map mappingToTraversalsMap = new HashMap();
+
+ public SimpleResourceMappingOperationInput(ResourceMapping[] mappings, ResourceMappingContext context) {
+ this.mappings = mappings;
+ this.context = context;
+ }
+
+ public ResourceMapping[] getSeedMappings() {
+ return mappings;
+ }
+
+ public void buildInput(IProgressMonitor monitor) throws CoreException {
+ buildInputMappingToTraversalsMap(monitor);
+ }
+
+ public ResourceMapping[] getInputMappings() {
+ return mappings;
+ }
+
+ public ResourceTraversal[] getInputTraversals() {
+ Collection values = mappingToTraversalsMap.values();
+ List result = new ArrayList();
+ for (Iterator iter = values.iterator(); iter.hasNext();) {
+ ResourceTraversal[] traversals = (ResourceTraversal[]) iter.next();
+ for (int i = 0; i < traversals.length; i++) {
+ ResourceTraversal traversal = traversals[i];
+ result.add(traversal);
+ }
+ }
+ return combineTraversals((ResourceTraversal[]) result.toArray(new ResourceTraversal[result.size()]));
+ }
+
+ public ResourceTraversal[] getTraversal(ResourceMapping mapping) {
+ return (ResourceTraversal[])mappingToTraversalsMap.get(mapping);
+ }
+
+ public boolean hasAdditionalMappings() {
+ return false;
+ }
+
+ private void buildInputMappingToTraversalsMap(IProgressMonitor monitor) throws CoreException {
+ monitor.beginTask(null, mappings.length * 100);
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ ResourceTraversal[] traversals = mapping.getTraversals(context, Policy.subMonitorFor(monitor, 100));
+ mappingToTraversalsMap.put(mapping, traversals);
+ }
+ monitor.done();
+ }
+
+ protected static ResourceTraversal[] combineTraversals(ResourceTraversal[] allTraversals) {
+ Set zero = new HashSet();
+ Set shallow = new HashSet();
+ Set deep = new HashSet();
+ for (int i = 0; i < allTraversals.length; i++) {
+ ResourceTraversal traversal = allTraversals[i];
+ switch (traversal.getDepth()) {
+ case IResource.DEPTH_ZERO:
+ zero.addAll(Arrays.asList(traversal.getResources()));
+ break;
+ case IResource.DEPTH_ONE:
+ shallow.addAll(Arrays.asList(traversal.getResources()));
+ break;
+ case IResource.DEPTH_INFINITE:
+ deep.addAll(Arrays.asList(traversal.getResources()));
+ break;
+ }
+ }
+ List result = new ArrayList();
+ if (!zero.isEmpty()) {
+ result.add(new ResourceTraversal((IResource[]) zero.toArray(new IResource[zero.size()]), IResource.DEPTH_ZERO, IResource.NONE));
+ }
+ if (!shallow.isEmpty()) {
+ result.add(new ResourceTraversal((IResource[]) shallow.toArray(new IResource[shallow.size()]), IResource.DEPTH_ONE, IResource.NONE));
+ }
+ if (!deep.isEmpty()) {
+ result.add(new ResourceTraversal((IResource[]) deep.toArray(new IResource[deep.size()]), IResource.DEPTH_INFINITE, IResource.NONE));
+ }
+ return (ResourceTraversal[]) result.toArray(new ResourceTraversal[result.size()]);
+ }
+
+ public ModelProvider[] getModelProviders() {
+ Set result = new HashSet();
+ ResourceMapping[] mappings = getInputMappings();
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ result.add(mapping.getModelProvider());
+ }
+ return (ModelProvider[]) result.toArray(new ModelProvider[result.size()]);
+ }
+
+ public ResourceMappingContext getContext() {
+ return context;
+ }
+
+ public ResourceMapping[] getMappings(ModelProvider provider) {
+ Set result = new HashSet();
+ ResourceMapping[] mappings = getInputMappings();
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ if (mapping.getModelProviderId().equals(provider.getDescriptor().getId())) {
+ result.add(mapping);
+ }
+ }
+ return (ResourceMapping[]) result.toArray(new ResourceMapping[result.size()]);
+ }
+
+ public ISynchronizeScope asSynchronizationScope() {
+ // TODO Temporary implementation
+ return new ResourceMappingScope("TODO: Need appropriate labels", getInputMappings(), getInputTraversals());
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/SynchronizationOperationLabelProvider.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/SynchronizationOperationLabelProvider.java
new file mode 100644
index 000000000..0aceb8f22
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/SynchronizationOperationLabelProvider.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.internal.ui.*;
+
+public abstract class SynchronizationOperationLabelProvider extends LabelProvider {
+
+ // Cache for folder images that have been overlayed with conflict icon
+ private Map fgImageCache;
+
+ // Contains direction images
+ CompareConfiguration compareConfig = new CompareConfiguration();
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getImage(java.lang.Object)
+ */
+ public Image getImage(Object element) {
+ LabelProvider modelLabelProvider = getModelLabelProvider();
+ Image base = modelLabelProvider.getImage(element);
+ if (base != null) {
+ int kind = getSyncKind(element);
+ Image decoratedImage;
+ decoratedImage = getCompareImage(base, kind);
+ // The reason we still overlay the compare image is to
+ // ensure that the image width for all images shown in the viewer
+ // are consistent.
+ return decoratedImage;
+ }
+ return base;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object)
+ */
+ public String getText(Object element) {
+ LabelProvider modelLabelProvider = getModelLabelProvider();
+ String base = modelLabelProvider.getText(element);
+ if (isSyncInfoInTextEnabled()) {
+ int kind = getSyncKind(element);
+ if (kind != SyncInfo.IN_SYNC) {
+ String syncKindString = SyncInfo.kindToString(kind);
+ return NLS.bind(TeamUIMessages.TeamSubscriberSyncPage_labelWithSyncKind, new String[] { base, syncKindString }); //
+ }
+ }
+ return base;
+ }
+
+ /**
+ * Returns whether the synchronization state should be included in the
+ * text of the label. By default, the Team preference is used to determine
+ * what to return.
+ * @return whether the synchronization state should be included in the
+ * text of the label
+ */
+ protected boolean isSyncInfoInTextEnabled() {
+ return TeamUIPlugin.getPlugin().getPreferenceStore().getBoolean(IPreferenceIds.SYNCVIEW_VIEW_SYNCINFO_IN_LABEL);
+ }
+
+ private Image getCompareImage(Image base, int kind) {
+ switch (kind & SyncInfo.DIRECTION_MASK) {
+ case SyncInfo.OUTGOING :
+ kind = (kind & ~SyncInfo.OUTGOING) | SyncInfo.INCOMING;
+ break;
+ case SyncInfo.INCOMING :
+ kind = (kind & ~SyncInfo.INCOMING) | SyncInfo.OUTGOING;
+ break;
+ }
+ return compareConfig.getImage(base, kind);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
+ */
+ public void dispose() {
+ compareConfig.dispose();
+ if (fgImageCache != null) {
+ Iterator it = fgImageCache.values().iterator();
+ while (it.hasNext()) {
+ Image element = (Image) it.next();
+ element.dispose();
+ }
+ }
+ }
+
+ /**
+ * Return the label provider that will return the text and image
+ * appropriate for the given model element. Subclasses are responsible for
+ * disposing of the label provider.
+ * @return the label provider that will return the text and image
+ * appropriate for the given model element
+ */
+ protected abstract LabelProvider getModelLabelProvider();
+
+ /**
+ * Return the sync kind of the given element. This is used
+ * to determine how to decorate the image and label of the
+ * element.
+ * @param element the element being tested
+ * @return the sync kind of the given element
+ */
+ protected abstract int getSyncKind(Object element);
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IDisposeListener.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IDisposeListener.java
new file mode 100644
index 000000000..261b023c5
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IDisposeListener.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+/**
+ * Listener that, when registered with a synchronization context, gets invoked
+ * when the context is disposed.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IDisposeListener {
+
+ /**
+ * The given context has been disposed.
+ */
+ void contextDisposed(ISynchronizeOperationContext context);
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IMergeContext.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IMergeContext.java
new file mode 100644
index 000000000..01f40c750
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IMergeContext.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.synchronize.SyncInfoSet;
+
+/**
+ * Provides the context for an <code>IResourceMappingMerger</code>
+ * or a model specific synchronization view that supports merging.
+ *
+ * TODO: Need to have a story for folder merging
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @see IResourceMappingMerger
+ * @since 3.2
+ */
+public interface IMergeContext extends ISynchronizeOperationContext {
+
+ /**
+ * Method that allows the model merger to signal that the file in question
+ * has been completely merged. Model mergers can call this method if they
+ * have transfered all changes from a remote file to a local file and wish
+ * to signal that the merge is done.This will allow repository providers to
+ * update the synchronization state of the file to reflect that the file is
+ * up-to-date with the repository.
+ * <p>
+ * Clients should not implement this interface but should instead subclass
+ * MergeContext.
+ *
+ * TODO: How are these batched? IWorkspace#run?
+ *
+ * @see MergeContext
+ *
+ * @param file the file that has been merged
+ * @param monitor a progress monitor
+ * @return a status indicating the results of the operation
+ */
+ public abstract IStatus markAsMerged(IFile file, IProgressMonitor monitor);
+
+ /**
+ * Method that can be called by the model merger to attempt a file-system
+ * level merge. This is useful for cases where the model merger does not
+ * need to do any special processing to perform the merge. By default, this
+ * method attempts to use an appropriate <code>IStreamMerger</code> to
+ * merge the files covered by the provided traversals. If a stream merger
+ * cannot be found, the text merger is used. If this behavior is not
+ * desired, sub-classes may override this method.
+ * <p>
+ * This method does a best-effort attempt to merge all the files covered
+ * by the provided traversals. Files that could not be merged will be
+ * indicated in the returned status. If the status returned has the code
+ * <code>MergeStatus.CONFLICTS</code>, the list of failed files can be
+ * obtained by calling the <code>MergeStatus#getConflictingFiles()</code>
+ * method.
+ * <p>
+ * Any resource changes triggered by this merge will be reported through the
+ * resource delta mechanism and the sync-info tree associated with this context.
+ *
+ * TODO: How do we handle folder removals generically?
+ *
+ * @see SyncInfoSet#addSyncSetChangedListener(ISyncInfoSetChangeListener)
+ * @see org.eclipse.core.resources.IWorkspace#addResourceChangeListener(IResourceChangeListener)
+ *
+ * @param infos
+ * the sync infos to be merged
+ * @param monitor
+ * a progress monitor
+ * @return a status indicating success or failure. A code of
+ * <code>MergeStatus.CONFLICTS</code> indicates that the file
+ * contain non-mergable conflicts and must be merged manually.
+ * @throws CoreException if an error occurs
+ */
+ public IStatus merge(SyncInfoSet infos, IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Method that can be called by the model merger to attempt a file level
+ * merge. This is useful for cases where the model merger does not need to
+ * do any special processing to perform the merge. By default, this method
+ * attempts to use an appropriate <code>IStreamMerger</code> to perform the
+ * merge. If a stream merger cannot be found, the text merger is used. If this behavior
+ * is not desired, sub-classes may override this method.
+ *
+ * @param file the file to be merged
+ * @param monitor a progress monitor
+ * @return a status indicating success or failure. A code of
+ * <code>MergeStatus.CONFLICTS</code> indicates that the file contain
+ * non-mergable conflicts and must be merged manually.
+ * @see org.eclipse.team.ui.mapping.IMergeContext#merge(org.eclipse.core.resources.IFile, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus merge(SyncInfo info, IProgressMonitor monitor);
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IResourceMappingMerger.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IResourceMappingMerger.java
new file mode 100644
index 000000000..4de3e0075
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IResourceMappingMerger.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import org.eclipse.core.runtime.*;
+
+/**
+ * The purpose of this interface is to provide support for model level
+ * auto-merging. It is helpful in the
+ * cases where a file may contain multiple model elements or a model element
+ * consists of multiple files. It can also be used for cases where there is a
+ * one-to-one mapping between model elements and files, although
+ * <code>IStreamMerger</code> can also be used in that case.
+ *
+ * Clients should group resource mappings by model provider and
+ * then attempt to obtain a merger from the model provider
+ * using the adaptable mechanism as follows:
+ *
+ * <pre>
+ * Object o = mapping.getModelProvider().getAdapter(IResourceMappingMerger.class);
+ * if (o instanceof IResourceMappingMerger.class) {
+ * IResourceMappingMerger merger = (IResourceMappingMerger)o;
+ * ...
+ * }
+ * </pre>
+ *
+ * If the model provider of the resource mappings does not adapt to
+ * <code>IResourceMappingMerger</code>, clients can obtain the merger
+ * from the Resources model provider {@link org.eclipse.core.resources.mapping.ResourceModelProvider}
+ * and use that.
+ * TODO: This is OK for now but will need to change
+ *
+ * @see org.eclipse.compare.IStreamMerger
+ * @see org.eclipse.core.resources.mapping.ResourceMapping
+ * @see org.eclipse.core.resources.mapping.ModelProvider
+ * @see org.eclipse.team.ui.mapping.IMergeContext
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface IResourceMappingMerger {
+
+ /**
+ * Attempt to automatically merge the mappings of the merge context(<code>MergeContext#getMappings()</code>).
+ * The merge context provides access to the out-of-sync resources (<code>MergeContext#getSyncInfoTree()</code>)
+ * associated with the mappings to be merged. The set of provided mappings
+ * may come from multiple model providers. A particular implementation of
+ * this interface should only merge the mappings associated with their model
+ * provider. Also, the set of resources may contain additional resources
+ * that are not part of the mappings being merged. Implementors of this
+ * interface should use the mappings to determine which resources to merge
+ * and what additional semantics can be used to attempt the merge.
+ * <p>
+ * The type of merge to be performed depends on what is returned by the
+ * <code>MergeContext#getType()</code> method. If the type is
+ * <code>MergeContext.TWO_WAY</code> the merge will replace the local
+ * contents with the remote contents, ignoring any local changes. For
+ * <code>THREE_WAY</code>, the base is used to attempt to merge remote
+ * changes with local changes.
+ * <p>
+ * If merging was not possible for one or more of the mappings to which this
+ * merge applies, these mappings should be returned in an
+ * <code>MergeStatus</code> whose code is
+ * <code>MergeStatus.CONFLICTS</code> and which provides access to the
+ * mappings which could not be merged. Note that it is up to the model to
+ * decide whether it wants to break one of the provided resource mappings
+ * into several sub-mappings and attempt auto-merging at that level.
+ *
+ * @param mappings the set of resource mappings being merged
+ * @param mergeContext a context that provides access to the resources
+ * involved in the merge. The context must not be
+ * <code>null</code>.
+ * @param monitor a progress monitor
+ * @return a status indicating the results of the operation. A code of
+ * <code>MergeStatus.CONFLICTS</code> indicates that some or all
+ * of the resource mappings could not be merged. The mappings that
+ * were not merged are available using
+ * <code>MergeStatus#getConflictingMappings()</code>
+ * @throws CoreException if errors occurred
+ */
+ public IStatus merge(IMergeContext mergeContext,
+ IProgressMonitor monitor) throws CoreException;
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IResourceMappingOperationInput.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IResourceMappingOperationInput.java
new file mode 100644
index 000000000..08b749651
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/IResourceMappingOperationInput.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.ui.synchronize.ISynchronizeScope;
+
+/**
+ * Interface which defines the protocol for translating
+ * a set of <code>ResourceMapping</code> objects representing
+ * a view selection into the complete set of resources to
+ * be operated on.
+ * <p>
+ * This interface is not intended to be implemented by clients
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @see org.eclipse.core.resources.mapping.ResourceMapping
+ *
+ * @since 3.2
+ */
+public interface IResourceMappingOperationInput {
+
+ /**
+ * Return the set of mappings that were selected
+ * when the operation was launched. These mappings
+ * are used to seed the input determination process.
+ * @return the set of mappings that were selected
+ * when the operation was launched
+ */
+ public ResourceMapping[] getSeedMappings();
+
+ /**
+ * Calculate the set of mappings to be operated on.
+ * This method must be called before <code>getInputMappings</code>
+ * or <code>getInputTraversals</code>.
+ *
+ * @param monitor a progress monitor
+ * @throws CoreException
+ */
+ public void buildInput(IProgressMonitor monitor) throws CoreException;
+
+ /**
+ * Return the complete set of mappings to be operated on.
+ * This method should only be invoked after <code>buildInput</code>
+ * is called.
+ * @return the complete set of mappings to be operated on
+ */
+ public ResourceMapping[] getInputMappings();
+
+ /**
+ * Return the set of traversals that cover the input
+ * resource mappings.
+ * This method should only be invoked after <code>buildInput</code>
+ * is called.
+ * @return the complete set of mappings to be operated on
+ */
+ public ResourceTraversal[] getInputTraversals();
+
+ /**
+ * Return the traversals that cover the given mapping.
+ * @param mapping a resource mapping being operated on
+ * @return the traversals that cover the given resource mapping
+ * (or <code>null</code> if the mapping is not contained in the input)
+ */
+ public ResourceTraversal[] getTraversal(ResourceMapping mapping);
+
+ /**
+ * Return whether the input has additional mappings added to the
+ * seed mappings.
+ * This method should only be invoked after <code>buildInput</code>
+ * is called.
+ * @return whether the input has additional mappings added to the
+ * seed mappings
+ */
+ public boolean hasAdditionalMappings();
+
+ public ModelProvider[] getModelProviders();
+
+ public ResourceMapping[] getMappings(ModelProvider provider);
+
+ public ISynchronizeScope asSynchronizationScope();
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ISynchronizationContext.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ISynchronizationContext.java
new file mode 100644
index 000000000..265eaff4e
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ISynchronizationContext.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.synchronize.SyncInfoSet;
+import org.eclipse.team.core.synchronize.SyncInfoTree;
+import org.eclipse.team.ui.synchronize.ISynchronizeScope;
+
+/**
+ * Allows a model provider to build a view of their model that includes
+ * synchronization information with a remote location (usually a repository).
+ * <p>
+ * The scope of the context is defined when the context is created. The creator
+ * of the scope may affect changes on the scope which will result in property
+ * change events from the scope and may result in sync-info change events from
+ * the sync-info tree. Clients should note that it is possible that a change in
+ * the scope will result in new out-of-sync resources being covered by the scope
+ * but not result in a sync-info change event from the sync-info tree. This can
+ * occur because the set may already have contained the out-of-sync resource
+ * with the understanding that the client would have ignored it. Consequently,
+ * clients should listen to both sources in order to guarantee that they update
+ * any dependent state appropriately.
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface ISynchronizationContext extends ITeamViewerContext {
+
+ /**
+ * Synchronization type constant that indicates that
+ * context is a two-way synchronization.
+ */
+ public final static String TWO_WAY = "two-way"; //$NON-NLS-1$
+
+ /**
+ * Synchronization type constant that indicates that
+ * context is a three-way synchronization.
+ */
+ public final static String THREE_WAY = "three-way"; //$NON-NLS-1$
+
+ /**
+ * Return the scope of this synchronization context. The scope determines
+ * the set of resources to which the context applies. Changes in the scope
+ * may result in changes to the sync-info available in the tree of this
+ * context.
+ *
+ * @return the set of mappings for which this context applies.
+ */
+ public ISynchronizeScope getScope();
+
+ /**
+ * Return a tree that contains <code>SyncInfo</code> nodes for resources
+ * that are out-of-sync. The tree will contain sync-info for any out-of-sync
+ * resources that are within the scope of this context. The tree
+ * may include additional out-of-sync resources, which should be ignored by
+ * the client. Clients can test for inclusion using the method
+ * {@link ISynchronizeScope#contains(IResource)}.
+ *
+ * @return a tree that contains a <code>SyncInfo</code> node for any
+ * resources that are out-of-sync.
+ */
+ public SyncInfoTree getSyncInfoTree();
+
+ /**
+ * Returns synchronization info for the given resource, or <code>null</code>
+ * if there is no synchronization info because the resource is not a
+ * candidate for synchronization.
+ * <p>
+ * Note that sync info may be returned for non-existing or for resources
+ * which have no corresponding remote resource.
+ * </p>
+ * <p>
+ * This method will be quick. If synchronization calculation requires content from
+ * the server it must be cached when the context is created or refreshed. A client should
+ * call refresh before calling this method to ensure that the latest information
+ * is available for computing the sync state.
+ * </p>
+ * @param resource the resource of interest
+ * @return sync info
+ * @throws CoreException
+ */
+ public SyncInfo getSyncInfo(IResource resource) throws CoreException;
+
+ /**
+ * Return the synchronization type. A type of <code>TWO_WAY</code>
+ * indicates that the synchronization information (i.e.
+ * <code>SyncInfo</code>) associated with the context will also be
+ * two-way (i.e. there is only a remote but no base involved in the
+ * comparison used to determine the synchronization state of resources. A
+ * type of <code>THREE_WAY</code> indicates that the synchronization
+ * information will be three-way and include the local, base (or ancestor)
+ * and remote.
+ *
+ * @return the type of merge to take place
+ *
+ * @see org.eclipse.team.core.synchronize.SyncInfo
+ */
+ public String getType();
+
+ /**
+ * Dispose of the synchronization context. This method should be
+ * invoked by clients when the context is no longer needed.
+ */
+ public void dispose();
+
+ /**
+ * Refresh the context in order to update the sync-info to include the
+ * latest remote state. any changes will be reported through the change
+ * listeners registered with the sync-info tree of this context. Changes to
+ * the set may be triggered by a call to this method or by a refresh
+ * triggered by some other source.
+ *
+ * @see SyncInfoSet#addSyncSetChangedListener(ISyncInfoSetChangeListener)
+ * @see org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent
+ *
+ * @param traversals the resource traversals which indicate which resources
+ * are to be refreshed
+ * @param flags additional refresh behavior. For instance, if
+ * <code>RemoteResourceMappingContext.FILE_CONTENTS_REQUIRED</code>
+ * is one of the flags, this indicates that the client will be
+ * accessing the contents of the files covered by the traversals.
+ * <code>NONE</code> should be used when no additional behavior
+ * is required
+ * @param monitor a progress monitor, or <code>null</code> if progress
+ * reporting is not desired
+ * @throws CoreException if the refresh fails. Reasons include:
+ * <ul>
+ * <li>The server could not be contacted for some reason (e.g.
+ * the context in which the operation is being called must be
+ * short running). The status code will be
+ * SERVER_CONTACT_PROHIBITED. </li>
+ * </ul>
+ */
+ public void refresh(ResourceTraversal[] traversals, int flags, IProgressMonitor monitor) throws CoreException;
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ISynchronizeOperationContext.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ISynchronizeOperationContext.java
new file mode 100644
index 000000000..dbe6cd6ca
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ISynchronizeOperationContext.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+/**
+ * A synchronization context that allows clients to cache operation
+ * state for the duration of the operation. When the context is disposed,
+ * the cache will be cleared.
+ * <p>
+ * This interface is not intended to be implemented by clients. Clients
+ * should instead subclass <@link org.eclipse.team.ui.mapping.SynchronizeOperationContext}
+ *
+ * @see org.eclipse.team.ui.mapping.SynchronizeOperationContext
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface ISynchronizeOperationContext extends ISynchronizationContext {
+
+ /**
+ * Cache the given property with this context.
+ * @param name the property name that uniquely identifies the property
+ * @param value the value to be cached.
+ */
+ void addProperty(String name, Object value);
+
+ /**
+ * Retrieve a property that has been cached with the context
+ * @param name the name of the property
+ * @return the object associated with the property name or <code>null</code>
+ */
+ Object getProperty(String name);
+
+ /**
+ * Remove the named property from the context
+ * @param name the property name
+ */
+ void removeProperty(String name);
+
+ /**
+ * Add a listener to the context that will receive notification
+ * when the context is disposed. Adding a listener that has already
+ * been added has no effect.
+ * @param listener the listener to add
+ */
+ void addDisposeListener(IDisposeListener listener);
+
+ /**
+ * Remove the listener. Removing a listener that is not registered
+ * has no effect.
+ * @param listener the listener to remove
+ */
+ void removeDisposeListener(IDisposeListener listener);
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ITeamViewerContext.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ITeamViewerContext.java
new file mode 100644
index 000000000..dd6115160
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ITeamViewerContext.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+
+/**
+ * A context determined by Team providers and passed to model views
+ * in order to display a subset of a model that is involved in a team
+ * operation
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public interface ITeamViewerContext {
+
+ public static final String ALL_MAPPINGS = "";
+
+ /**
+ * Return the model providers that have mappings
+ * in this context.
+ * @return the model providers that have mappings
+ * in this context
+ */
+ public ModelProvider[] getModelProviders();
+
+ /**
+ * Return the set of resource mappings associated with
+ * the given model provider affected by the
+ * team operation that provided the context. If all
+ * resource mappings are desired, pass <code>ALL_MAPPINGS</code>
+ * as the model provider id.
+ * @param modelProviderId the model provider id.
+ * @return a set of resource mappings
+ */
+ public ResourceMapping[] getResourceMappings(String modelProviderId);
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/MergeContext.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/MergeContext.java
new file mode 100644
index 000000000..ed6351a9e
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/MergeContext.java
@@ -0,0 +1,399 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.IStreamMerger;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.team.core.synchronize.*;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.internal.core.TeamPlugin;
+import org.eclipse.team.internal.ui.TeamUIPlugin;
+
+/**
+ * Provides the context for an <code>IResourceMappingMerger</code>.
+ * It provides access to the ancestor and remote resource mapping contexts
+ * so that resource mapping mergers can attempt head-less auto-merges.
+ * The ancestor context is only required for merges while the remote
+ * is required for both merge and replace.
+ *
+ * TODO: Need to have a story for folder merging
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @see IResourceMappingMerger
+ * @since 3.2
+ */
+public abstract class MergeContext extends SynchronizeOperationContext implements IMergeContext {
+
+ private static final String TXT_EXTENTION = "txt"; //$NON-NLS-1$
+
+ private final String type;
+ private final SyncInfoTree tree;
+
+ /**
+ * Create a merge context.
+ * @param type
+ */
+ protected MergeContext(String type, SyncInfoTree tree, IResourceMappingOperationInput input) {
+ super(input);
+ this.type = type;
+ this.tree = tree;
+ }
+
+ /**
+ * Return the type of merge to take place. A
+ * type of <code>TWO_WAY</code> indicates that
+ * the local contents are to be replaced with
+ * the remote contents while a type of <code>THREE_WAY</code>
+ * indicates that the remote changes should be merged
+ * with the local changes.
+ * @return the type of merge to take place
+ */
+ public final String getType() {
+ return type;
+ }
+
+ /**
+ * Return a tree that contains a <code>SyncInfo</code>
+ * node for any resources that are out-of-sync.
+ * @return a tree that contains a <code>SyncInfo</code>
+ * node for any resources that are out-of-sync.
+ */
+ public final SyncInfoTree getSyncInfoTree() {
+ return tree;
+ }
+
+ /**
+ * Method that allows the model merger to signal that the file in question
+ * has been completely merged. Model mergers can call this method if they
+ * have transfered all changes from a remote file to a local file and wish
+ * to signal that the merge is done.This will allow repository providers to
+ * update the synchronization state of the file to reflect that the file is
+ * up-to-date with the repository.
+ *
+ * @param file the file that has been merged
+ * @param monitor a progress monitor
+ * @return a status indicating the results of the operation
+ */
+ public abstract IStatus markAsMerged(IFile file, IProgressMonitor monitor);
+
+ /**
+ * Method that can be called by the model merger to attempt a file-system
+ * level merge. This is useful for cases where the model merger does not
+ * need to do any special processing to perform the merge. By default, this
+ * method attempts to use an appropriate <code>IStreamMerger</code> to
+ * merge the files covered by the provided traversals. If a stream merger
+ * cannot be found, the text merger is used. If this behavior is not
+ * desired, sub-classes may override this method.
+ * <p>
+ * This method does a best-effort attempt to merge all the files covered
+ * by the provided traversals. Files that could not be merged will be
+ * indicated in the returned status. If the status returned has the code
+ * <code>MergeStatus.CONFLICTS</code>, the list of failed files can be
+ * obtained by calling the <code>MergeStatus#getConflictingFiles()</code>
+ * method.
+ * <p>
+ * Any resource changes triggered by this merge will be reported through the
+ * resource delta mechanism and the sync-info tree associated with this context.
+ *
+ * TODO: How do we handle folder removals generically?
+ *
+ * @see SyncInfoSet#addSyncSetChangedListener(ISyncInfoSetChangeListener)
+ * @see org.eclipse.core.resources.IWorkspace#addResourceChangeListener(IResourceChangeListener)
+ *
+ * @param infos
+ * the array of sync info to be merged
+ * @param monitor
+ * a progress monitor
+ * @return a status indicating success or failure. A code of
+ * <code>MergeStatus.CONFLICTS</code> indicates that the file
+ * contain non-mergable conflicts and must be merged manually.
+ * @throws CoreException if an error occurs
+ */
+ public IStatus merge(SyncInfoSet infos, IProgressMonitor monitor) throws CoreException {
+ List failedFiles = new ArrayList();
+ for (Iterator iter = infos.iterator(); iter.hasNext();) {
+ SyncInfo info = (SyncInfo) iter.next();
+ IStatus s = merge(info, monitor);
+ if (!s.isOK()) {
+ if (s.getCode() == MergeStatus.CONFLICTS) {
+ failedFiles.addAll(Arrays.asList(((MergeStatus)s).getConflictingFiles()));
+ } else {
+ return s;
+ }
+ }
+ }
+ if (failedFiles.isEmpty()) {
+ return Status.OK_STATUS;
+ } else {
+ return new MergeStatus(TeamPlugin.ID, "Could not merge all files", (IFile[]) failedFiles.toArray(new IFile[failedFiles.size()]));
+ }
+ }
+
+ /**
+ * Method that can be called by the model merger to attempt a file level
+ * merge. This is useful for cases where the model merger does not need to
+ * do any special processing to perform the merge. By default, this method
+ * attempts to use an appropriate <code>IStreamMerger</code> to perform the
+ * merge. If a stream merger cannot be found, the text merger is used. If this behavior
+ * is not desired, sub-classes may override this method.
+ *
+ * @param file the file to be merged
+ * @param monitor a progress monitor
+ * @return a status indicating success or failure. A code of
+ * <code>MergeStatus.CONFLICTS</code> indicates that the file contain
+ * non-mergable conflicts and must be merged manually.
+ * @see org.eclipse.team.ui.mapping.MergeContext#merge(org.eclipse.core.resources.IFile, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus merge(SyncInfo info, IProgressMonitor monitor) {
+ IResource r = info.getLocal();
+ if (r.getType() != IResource.FILE)
+ return Status.OK_STATUS;
+ IFile file = (IFile)r;
+ try {
+ if (info.getComparator().isThreeWay()) {
+ int direction = SyncInfo.getDirection(info.getKind());
+ if (direction == SyncInfo.OUTGOING) {
+ // There's nothing to do so return OK
+ return Status.OK_STATUS;
+ }
+ if (direction == SyncInfo.INCOMING) {
+ // Just copy the stream since there are no conflicts
+ return performReplace(info, monitor);
+ }
+ // direction == SyncInfo.CONFLICTING
+ int type = SyncInfo.getChange(info.getKind());
+ if (type == SyncInfo.DELETION) {
+ // Nothing needs to be done although subclasses
+ markAsMerged(file, monitor);
+ return Status.OK_STATUS;
+ }
+ // type == SyncInfo.CHANGE
+ IResourceVariant base = info.getBase();
+ IResourceVariant remote = info.getRemote();
+ if (base == null || remote == null || !file.exists()) {
+ // Nothing we can do so return a conflict status
+ // TODO: Should we handle the case where the local and remote have the same contents for a conflicting addition?
+ return new MergeStatus(TeamUIPlugin.ID, NLS.bind("Conflicting change could not be merged: {0}", new String[] { file.getFullPath().toString() }), new IFile[] { file });
+ }
+ // We have a conflict, a local, base and remote so we can do
+ // a three-way merge
+ return performThreeWayMerge(info, monitor);
+ } else {
+ return performReplace(info, monitor);
+ }
+ } catch (CoreException e) {
+ return new Status(IStatus.ERROR, TeamPlugin.ID, MergeStatus.INTERNAL_ERROR, NLS.bind("Merge of {0} failed due to an internal error.", new String[] { file.getFullPath().toString() }), e);
+ }
+ }
+
+ /*
+ * Replace the local contents with the remote contents.
+ * The local resource must be a file.
+ */
+ private IStatus performReplace(SyncInfo info, IProgressMonitor monitor) throws CoreException {
+ IFile file = (IFile)info.getLocal();
+ IResourceVariant remote = info.getRemote();
+ if (remote == null && file.exists()) {
+ file.delete(false, true, monitor);
+ } else if (remote != null) {
+ InputStream stream = remote.getStorage(monitor).getContents();
+ stream = new BufferedInputStream(stream);
+ try {
+ if (file.exists()) {
+ file.setContents(stream, false, true, monitor);
+ } else {
+ file.create(stream, false, monitor);
+ }
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ markAsMerged(file, monitor);
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * Perform a three-way merge on the given sync-info.
+ * The local resource must be a file and all three
+ * resources (local, base, remote) must exist.
+ */
+ private IStatus performThreeWayMerge(SyncInfo info, IProgressMonitor monitor) throws CoreException {
+ IFile file = (IFile)info.getLocal();
+ IContentDescription contentDescription = file.getContentDescription();
+ IStreamMerger merger = null;
+ if (contentDescription != null && contentDescription.getContentType() != null) {
+ merger = CompareUI.createStreamMerger(contentDescription.getContentType());
+ } else {
+ String fileExtension = file.getFileExtension();
+ if (fileExtension != null)
+ merger = CompareUI.createStreamMerger(fileExtension);
+ }
+ // If we couldn't find a registered merger, fallback to text
+ // since we know we have one of those registered.
+ if (merger == null)
+ merger = CompareUI.createStreamMerger(TXT_EXTENTION);
+ if (merger == null)
+ return new Status(IStatus.ERROR, TeamPlugin.ID, MergeStatus.INTERNAL_ERROR, NLS.bind("Auto-merge support for {0} is not available.", new String[] { file.getFullPath().toString() }), null);
+ return merge(merger, info, monitor);
+ }
+
+ /*
+ * Perform a three-way merge on the given sync-info using the given
+ * stream merger. The local resource must be a file and all three
+ * resources (local, base, remote) must exist.
+ */
+ private IStatus merge(IStreamMerger merger, SyncInfo info, IProgressMonitor monitor) throws CoreException {
+
+ // Get the file involved
+ IFile file = (IFile)info.getLocal();
+
+ // Define all the input streams here so we can ensure they get closed
+ InputStream ancestorStream = null;
+ InputStream remoteStream = null;
+ InputStream targetStream = null;
+
+ try {
+
+
+ // Get the ancestor stream and encoding
+ IResourceVariant base = info.getBase();
+ IStorage s = base.getStorage(monitor);
+ String ancestorEncoding = null;
+ if (s instanceof IEncodedStorage) {
+ IEncodedStorage es = (IEncodedStorage) s;
+ ancestorEncoding = es.getCharset();
+ }
+ if (ancestorEncoding == null) {
+ ancestorEncoding = file.getCharset();
+ }
+ ancestorStream = new BufferedInputStream(s.getContents());
+
+ // Get the remote stream and encoding
+ IResourceVariant remote = info.getRemote();
+ s = remote.getStorage(monitor);
+ String remoteEncoding = null;
+ if (s instanceof IEncodedStorage) {
+ IEncodedStorage es = (IEncodedStorage) s;
+ remoteEncoding = es.getCharset();
+ }
+ if (remoteEncoding == null) {
+ remoteEncoding = file.getCharset();
+ }
+ remoteStream = new BufferedInputStream(s.getContents());
+
+ // Get the local (target) stream and encoding
+ targetStream = file.getContents();
+ String targetEncoding = file.getCharset();
+ IStatus status;
+ OutputStream output = getTempOutputStream(file);
+ try {
+ status = merger.merge(output, targetEncoding, ancestorStream, ancestorEncoding, targetStream, targetEncoding, remoteStream, remoteEncoding, monitor);
+ if (status.isOK()) {
+ file.setContents(getTempInputStream(file, output), false, true, monitor);
+ markAsMerged(file, monitor);
+ }
+ } finally {
+ disposeTempOutputStream(file, output);
+ }
+ return status;
+ } finally {
+ try {
+ if (ancestorStream != null)
+ ancestorStream.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ try {
+ if (remoteStream != null)
+ remoteStream.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ try {
+ if (targetStream != null)
+ targetStream.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+
+ private InputStream getTempInputStream(IFile file, OutputStream output) throws CoreException {
+ if (output instanceof ByteArrayOutputStream) {
+ ByteArrayOutputStream baos = (ByteArrayOutputStream) output;
+ return new ByteArrayInputStream(baos.toByteArray());
+ }
+ // We created a temporary file so we need to open an input stream on it
+ try {
+ // First make sure the output stream is closed
+ if (output != null)
+ output.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ File tmpFile = getTempFile(file);
+ try {
+ return new BufferedInputStream(new FileInputStream(tmpFile));
+ } catch (FileNotFoundException e) {
+ throw new CoreException(new Status(IStatus.ERROR, TeamPlugin.ID, MergeStatus.INTERNAL_ERROR, NLS.bind("Could not read from temporary file {0}: {1}", new String[] { tmpFile.getAbsolutePath(), e.getMessage() }), e));
+ }
+ }
+
+ private void disposeTempOutputStream(IFile file, OutputStream output) {
+ if (output instanceof ByteArrayOutputStream)
+ return;
+ // We created a temporary file so we need to clean it up
+ try {
+ // First make sure the output stream is closed
+ // so that file deletion will not fail because of that.
+ if (output != null)
+ output.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ File tmpFile = getTempFile(file);
+ if (tmpFile.exists())
+ tmpFile.delete();
+ }
+
+ private OutputStream getTempOutputStream(IFile file) throws CoreException {
+ File tmpFile = getTempFile(file);
+ if (tmpFile.exists())
+ tmpFile.delete();
+ try {
+ return new BufferedOutputStream(new FileOutputStream(tmpFile));
+ } catch (FileNotFoundException e) {
+ TeamPlugin.log(IStatus.ERROR, NLS.bind("Could not open temporary file {0} for writing: {1}", new String[] { tmpFile.getAbsolutePath(), e.getMessage() }), e);
+ return new ByteArrayOutputStream();
+ }
+ }
+
+ private File getTempFile(IFile file) {
+ return TeamPlugin.getPlugin().getStateLocation().append(".tmp").append(file.getName() + ".tmp").toFile(); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/MergeStatus.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/MergeStatus.java
new file mode 100644
index 000000000..4c20a2ab9
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/MergeStatus.java
@@ -0,0 +1,88 @@
+package org.eclipse.team.ui.mapping;
+
+import org.eclipse.compare.IStreamMerger;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * A special status that is returned when the return code
+ * of the <code>merge</code> method is <code>CONFLICTS</code>.
+ * It is possible that there were problems that caused the
+ * auto-merge to fail. In that case, the implementor of
+ * <code>IResourceMappingMerger</code> can return a multi-status
+ * in which one of the children is a <code>MergeStatus</code> and
+ * the others describe other problems that were encountered.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @see org.eclipse.team.ui.mapping.IResourceMappingMerger
+ *
+ * @since 3.2
+ */
+public final class MergeStatus extends Status {
+
+ private ResourceMapping[] conflictingMappings;
+ private IFile[] conflictingFiles;
+
+ /**
+ * Create a merge status for reporting that some of the resource mappings
+ * for which a merge was attempted were not auto-mergable.
+ * @param pluginId the plugin id
+ * @param message the message for the status
+ * @param conflictingMappings the mappings which were not auto-mergable
+ */
+ public MergeStatus(String pluginId, String message, ResourceMapping[] conflictingMappings) {
+ super(IStatus.ERROR, pluginId, CONFLICTS, message, null);
+ this.conflictingMappings = conflictingMappings;
+ }
+
+ /**
+ * Create a merge status for reporting that some of the files
+ * for which a merge was attempted were not auto-mergable.
+ * @param pluginId the plugin id
+ * @param message the message for the status
+ * @param files the files which were not auto-mergable
+ */
+ public MergeStatus(String pluginId, String message, IFile[] files) {
+ super(IStatus.ERROR, pluginId, CONFLICTS, message, null);
+ this.conflictingFiles = files;
+ }
+
+ /**
+ * Indicates that a change conflict prevented some or all of the resource
+ * mappings to be merged (value <code>1</code>). When this code is
+ * returned, the status must be of type
+ * <code>MergeStatus</code> and must contain the list of all
+ * resource mappings for which a manual merge is required.
+ */
+ public static final int CONFLICTS = IStreamMerger.CONFLICT;
+
+ /**
+ * Status code describing an internal error (value <code>2</code>).
+ * The status return is not required to be of type <code>MergeStatus</code>
+ * for internal errors.
+ */
+ public static final int INTERNAL_ERROR= IStreamMerger.INTERNAL_ERROR;
+
+ /**
+ * Returns the set of resource mappings for which an auto-merge was
+ * not performed. The client should present the mappings to the user
+ * in a manner that will allow the user to perform a manual merges.
+ * @return the set of resource mappings for which an auto-merge was
+ * not performed.
+ */
+ public ResourceMapping[] getConflictingMappings() {
+ return conflictingMappings;
+ }
+
+ public IFile[] getConflictingFiles() {
+ return conflictingFiles;
+ }
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingMergeOperation.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingMergeOperation.java
new file mode 100644
index 000000000..12e601e19
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingMergeOperation.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.runtime.*;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ui.Policy;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * The steps of an optimistic merge operation are:
+ * <ol>
+ * <li>Obtain the selection to be operated on.
+ * <li>Determine the projection of the selection onto resources
+ * using resource mappings and traversals.
+ * <ul>
+ * <li>this will require traversals using both the ancestor and remote
+ * for three-way merges.
+ * <li>for model providers with registered merger, mapping set need
+ * not be expanded (this is tricky if one of the model providers doesn't
+ * have a merge but all others do).
+ * <li>if the model does not have a custom merger, ensure that additional
+ * mappings are included (i.e. for many model elements to one resource case)
+ * </ul>
+ * <li>Create a MergeContext for the merge
+ * <ul>
+ * <li>Determine the synchronization state of all resources
+ * covered by the input.
+ * <li>Pre-fetch the required contents.
+ * </ul>
+ * <li>Obtain and invoke the merger for each provider
+ * <ul>
+ * <li>This will auto-merge as much as possible
+ * <li>If everything was merged, cleanup and stop
+ * <li>Otherwise, a set of un-merged resource mappings is returned
+ * </ul>
+ * <li>Delegate manual merge to the model provider
+ * <ul>
+ * <li>This hands off the context to the manual merge
+ * <li>Once completed, the manual merge must clean up
+ * </ul>
+ * </ol>
+ *
+ * <p>
+ * Handle multiple model providers where one extends all others by using
+ * the top-most model provider. The assumption is that the model provider
+ * will delegate to lower level model providers when appropriate.
+ * <p>
+ * Special case to support sub-file merges.
+ * <ul>
+ * <li>Restrict when sub-file merging is supported
+ * <ul>
+ * <li>Only one provider involved (i.e. consulting participants results
+ * in participants that are from the model provider or below).
+ * <li>The provider has a custom auto and manual merger.
+ * </ul>
+ * <li>Prompt to warn when sub-file merging is not possible.
+ * <li>Need to display the additional elements that will be affected.
+ * This could be done in a diff tree or some other view. It needs to
+ * consider incoming changes including additions.
+ * </ul>
+ * <p>
+ * Special case to handle conflicting model providers.
+ * <ul>
+ * <li>Prompt user to indicate the conflict
+ * <li>Allow user to exclude one of the models?
+ * <li>Allow use to choose order of evaluation?
+ * <li>Support tabbed sync view
+ * </ul>
+ * <p>
+ * TODO: What about support for down grading a merge. That is, the user wants
+ * to perform the merge at the file level even though a higher level model owns
+ * the files. We could provide a preference that the user can set to perform
+ * the merge at the level selected and not involve participants.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class ResourceMappingMergeOperation extends ResourceMappingOperation {
+
+ protected ResourceMappingMergeOperation(IWorkbenchPart part, IResourceMappingOperationInput input) {
+ super(part, input);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.mapping.ResourceMappingOperation#execute(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ protected void execute(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ try {
+ IMergeContext context = buildMergeContext(monitor);
+ ModelProvider[] providers = getInput().getModelProviders();
+ List failedMerges = new ArrayList();
+ for (int i = 0; i < providers.length; i++) {
+ ModelProvider provider = providers[i];
+ if (!performMerge(provider, context, monitor)) {
+ failedMerges.add(provider);
+ }
+ }
+ if (failedMerges.isEmpty()) {
+ context.dispose();
+ } else {
+ requiresManualMerge((ModelProvider[]) failedMerges.toArray(new ModelProvider[failedMerges.size()]), context);
+ }
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ /**
+ * One or more of the model elements for the given providers
+ * requires a manual merge. When the manual merge is
+ * @param providers the providers
+ * @param context the merge context
+ * @throws CoreException
+ */
+ protected abstract void requiresManualMerge(ModelProvider[] providers, IMergeContext context) throws CoreException;
+
+ /**
+ * Build and initialize a merge context for the input of this operation.
+ * @param monitor a progress monitor
+ * @return a merge context for merging the mappings of the input
+ */
+ protected abstract IMergeContext buildMergeContext(IProgressMonitor monitor);
+
+ /**
+ * Merge all the mappings that come from the given provider. By default,
+ * an automatic merge is attempted. After this, a manual merge (i.e. with user
+ * intervention) is attempted on any mappings that could not be merged
+ * automatically.
+ * @param provider the model provider
+ * @param mappings the mappings to be merged
+ * @param monitor a progress monitor
+ * @throws CoreException
+ */
+ protected boolean performMerge(ModelProvider provider, IMergeContext mergeContext, IProgressMonitor monitor) throws CoreException {
+ try {
+ monitor.beginTask(null, 100);
+ IStatus status = performAutoMerge(provider, mergeContext, Policy.subMonitorFor(monitor, 95));
+ if (!status.isOK()) {
+ if (status.getCode() == MergeStatus.CONFLICTS) {
+ return false;
+ } else {
+ throw new TeamException(status);
+ }
+ }
+ } finally {
+ monitor.done();
+ }
+ return true;
+ }
+
+ /**
+ * Attempt to merge automatically. The returned status will indicate which
+ * mappings could not be merged automatically.
+ * @param provider the provider for the mappings being merged
+ * @param mergeContext the context for the merge
+ * @param monitor a progress monitor
+ * @return a status indicating success or failure. A failure status
+ * will be a MergeStatus that includes the mappings that could not be merged.
+ * @throws CoreException if errors occurred
+ */
+ protected IStatus performAutoMerge(ModelProvider provider, IMergeContext mergeContext, IProgressMonitor monitor) throws CoreException {
+ IResourceMappingMerger merger = getMerger(provider);
+ IStatus status = merger.merge(mergeContext, monitor);
+ return status;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingOperation.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingOperation.java
new file mode 100644
index 000000000..882ac1540
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingOperation.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.team.internal.ui.dialogs.AdditionalMappingsDialog;
+import org.eclipse.team.internal.ui.mapping.DefaultResourceMappingMerger;
+import org.eclipse.team.ui.TeamOperation;
+import org.eclipse.ui.IWorkbenchPart;
+
+/**
+ * Here's a summary of the input determination scheme
+ * <ol>
+ * <li>Obtain selected mappings
+ * <li>Project mappings onto resources using the appropriate
+ * context(s) in order to obtain a set of ResourceTraverals
+ * <li>Determine what model providers are interested in the targeted resources
+ * <li>From those model providers, obtain the set of affected resource mappings
+ * <li>If the original set is the same as the new set, we are done.
+ * <li>if the set differs from the original selection, rerun the mapping process
+ * for any new mappings
+ * <ul>
+ * <li>Only need to query model providers for mappings for new resources
+ * <li>If new mappings are obtained,
+ * ask model provider to compress the mappings?
+ * <li>keep repeating until no new mappings or resources are added
+ * </ul>
+ * <li>Use model provider relationships to result?
+ * <li>Display the original set and the new set with an explanation
+ * <ul>
+ * <li>The original set and final set may involve mappings from
+ * multiple providers.
+ * <li>The number of providers can be reduced by assuming that
+ * extending models can display the elements of extended models.
+ * Then we are only left with conflicting models.
+ * <li>Could use a content provider approach a.k.a. Common Navigator
+ * or component based approach
+ * </ul>
+ * </ol>
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public abstract class ResourceMappingOperation extends TeamOperation {
+
+ private final IResourceMappingOperationInput input;
+
+ /**
+ * Create a resource mapping based operation
+ * @param part the workspace part from which the operation was launched
+ * @param input the input to the operation (which must have already been built by
+ * invoking <code>buildInput</code>.
+ */
+ protected ResourceMappingOperation(IWorkbenchPart part, IResourceMappingOperationInput input) {
+ super(part);
+ this.input = input;
+ }
+
+ public void run(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException {
+ buildInput(monitor);
+ execute(monitor);
+ }
+
+ /**
+ * Adjust the input of the operation according to the selected
+ * resource mappings and the set of interested participants
+ * @param monitor
+ */
+ protected void buildInput(IProgressMonitor monitor) throws InvocationTargetException {
+ try {
+ input.buildInput(monitor);
+ if (input.hasAdditionalMappings()) {
+ promptForInputChange(monitor);
+ }
+ } catch (CoreException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ /**
+ * Prompt the user to inform them that additional resource mappings
+ * have been included in the operations.
+ * @param monitor a progress monitor
+ * @throws OperationCanceledException if the user choose to cancel
+ */
+ protected void promptForInputChange(IProgressMonitor monitor) {
+ showAllMappings(input.getSeedMappings(), input.getInputMappings());
+ }
+
+ private void showAllMappings(final ResourceMapping[] selectedMappings, final ResourceMapping[] allMappings) {
+ final boolean[] canceled = new boolean[] { false };
+ getShell().getDisplay().syncExec(new Runnable() {
+ public void run() {
+ AdditionalMappingsDialog dialog = new AdditionalMappingsDialog(getShell(), "Participating Elements", selectedMappings, new TeamViewerContext(allMappings));
+ int result = dialog.open();
+ canceled[0] = result != Dialog.OK;
+ }
+
+ });
+
+ if (canceled[0]) {
+ throw new OperationCanceledException();
+ }
+ }
+
+ protected abstract void execute(IProgressMonitor monitor) throws InvocationTargetException,
+ InterruptedException;
+
+ /**
+ * Return the auto-merger associated with the given model provider
+ * view the adaptable mechanism.
+ * If the model provider does not have a merger associated with
+ * it, a default merger that performs the merge at the file level
+ * is returned.
+ * @param provider the model provider of the elements to be merged
+ * @return a merger
+ */
+ protected IResourceMappingMerger getMerger(ModelProvider provider) {
+ Object o = provider.getAdapter(IResourceMappingMerger.class);
+ if (o instanceof IResourceMappingMerger) {
+ return (IResourceMappingMerger) o;
+ }
+ return new DefaultResourceMappingMerger(provider, getInput());
+ }
+
+ public IResourceMappingOperationInput getInput() {
+ return input;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingOperationInput.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingOperationInput.java
new file mode 100644
index 000000000..59dc4fd4b
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/ResourceMappingOperationInput.java
@@ -0,0 +1,251 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+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.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.IModelProviderDescriptor;
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.resources.mapping.ResourceMappingContext;
+import org.eclipse.core.resources.mapping.ResourceTraversal;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.internal.ui.Policy;
+import org.eclipse.team.internal.ui.TeamUIPlugin;
+import org.eclipse.team.internal.ui.mapping.SimpleResourceMappingOperationInput;
+
+/**
+ * Transform the set of selected resource mappings into the
+ * complete set of resource mappings affected by the operation.
+ * <p>
+ * Here's a summary of the input determination scheme
+ * <ol>
+ * <li>Obtain selected mappings
+ * <li>Project mappings onto resources using the appropriate
+ * context(s) in order to obtain a set of ResourceTraverals
+ * <li>Determine what model providers are interested in the targeted resources
+ * <li>From those model providers, obtain the set of affected resource mappings
+ * <li>If the original set is the same as the new set, we are done.
+ * <li>if the set differs from the original selection, rerun the mapping process
+ * for any new mappings
+ * <ul>
+ * <li>Only need to query model providers for mappings for new resources
+ * <li>If new mappings are obtained,
+ * ask model provider to compress the mappings?
+ * <li>keep repeating until no new mappings or resources are added
+ * </ul>
+ * <li>Compress the mappings from each provider
+ * <li>flag overlapping mappings from independent providers
+ * <li>Display the original set and the new set with an explanation
+ * <ul>
+ * <li>The original set and final set may involve mappings from
+ * multiple providers.
+ * <li>The number of providers can be reduced by assuming that
+ * extending models can display the elements of extended models.
+ * Then we are only left with conflicting models.
+ * <li>Could use a content provider approach a.k.a. Common Navigator
+ * or component based approach
+ * </ul>
+ * </ol>
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class ResourceMappingOperationInput extends SimpleResourceMappingOperationInput {
+
+ private final Map inputMappingsToResources = new HashMap();
+ private final Map targetMappingsToResources = new HashMap();
+ private boolean hasAdditionalMappings;
+
+ public ResourceMappingOperationInput(ResourceMapping[] mappings, ResourceMappingContext context) {
+ super(mappings, context);
+ }
+
+ public void buildInput(IProgressMonitor monitor) throws CoreException {
+ monitor.beginTask(null, IProgressMonitor.UNKNOWN);
+ buildInputMappingToResourcesMap(Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
+ Set targetMappings = inputMappingsToResources.keySet();
+ Set handledResources = new HashSet();
+ Set newResources;
+ do {
+ newResources = addToTargetMappingToResourceMap(targetMappings, Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
+ IResource[] adjusted = adjustNewResources(newResources);
+ targetMappings = internalGetMappingsFromProviders(adjusted, getAffectedNatures(targetMappings), Policy.subMonitorFor(monitor, IProgressMonitor.UNKNOWN));
+
+ //TODO: The new resources aren't really just the new ones so reduce the set if needed
+ if (!handledResources.isEmpty()) {
+ for (Iterator iter = newResources.iterator(); iter.hasNext();) {
+ IResource resource = (IResource) iter.next();
+ if (handledResources.contains(resource)) {
+ iter.remove();
+ }
+ }
+ }
+
+ handledResources.addAll(newResources);
+
+ } while (!newResources.isEmpty());
+ hasAdditionalMappings = internalHasAdditionalMappings();
+ }
+
+ /*
+ * Give the subclass a chance to add resources to the set of affected resources
+ */
+ private IResource[] adjustNewResources(Set newResources) {
+ IResource[] resources = (IResource[]) newResources.toArray(new IResource[newResources.size()]);
+ IResource[] adjusted = adjustInputResources(resources);
+ return adjusted;
+ }
+
+ /**
+ * Adjust the given set of input resources to include any additional
+ * resources required by a particular repository provider for the current
+ * operation. By default the original set is returned but subclasses may
+ * override. Overriding methods should return a set of resources that
+ * include the original resource either explicitly or implicitly as a child
+ * of a returned resource.
+ *
+ * @param resources the input resources
+ * @return the input resources adjusted to include any additional resources
+ * required for the current operation
+ */
+ protected IResource[] adjustInputResources(IResource[] resources) {
+ return resources;
+ }
+
+ private boolean internalHasAdditionalMappings() {
+ ResourceMapping[] inputMappings = getSeedMappings();
+ if (inputMappings .length == targetMappingsToResources.size()) {
+ for (int i = 0; i < inputMappings.length; i++) {
+ ResourceMapping mapping = inputMappings[i];
+ if (!targetMappingsToResources.containsKey(mapping)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ private String[] getAffectedNatures(Set targetMappings) {
+ Set result = new HashSet();
+ for (Iterator iter = targetMappings.iterator(); iter.hasNext();) {
+ ResourceMapping mapping = (ResourceMapping) iter.next();
+ IProject[] projects = mapping.getProjects();
+ for (int j = 0; j < projects.length; j++) {
+ IProject project = projects[j];
+ try {
+ result.addAll(Arrays.asList(project.getDescription().getNatureIds()));
+ } catch (CoreException e) {
+ TeamUIPlugin.log(e);
+ }
+ }
+ }
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+ private Set internalGetMappingsFromProviders(IResource[] resources, String[] affectedNatures, IProgressMonitor monitor) throws CoreException {
+ Set result = new HashSet();
+ IModelProviderDescriptor[] descriptors = ModelProvider.getModelProviderDescriptors();
+ for (int i = 0; i < descriptors.length; i++) {
+ IModelProviderDescriptor descriptor = descriptors[i];
+ ResourceMapping[] mappings = getMappings(descriptor, resources, affectedNatures, getContext(), monitor);
+ result.addAll(Arrays.asList(mappings));
+ }
+ return result;
+ }
+
+ public ResourceMapping[] getMappings(IModelProviderDescriptor descriptor, IResource[] resources, String[] affectedNatures, ResourceMappingContext context, IProgressMonitor monitor) throws CoreException {
+ IResource[] matchingResources = descriptor.getMatchingResources(resources, affectedNatures);
+ return descriptor.getModelProvider().getMappings(matchingResources, context, monitor);
+ }
+
+ private Set addToTargetMappingToResourceMap(Set targetMappings, IProgressMonitor monitor) throws CoreException {
+ Set newResources = new HashSet();
+ for (Iterator iter = targetMappings.iterator(); iter.hasNext();) {
+ ResourceMapping mapping = (ResourceMapping) iter.next();
+ if (!targetMappingsToResources.containsKey(mapping)) {
+ ResourceTraversal[] traversals = mapping.getTraversals(getContext(), Policy.subMonitorFor(monitor, 100));
+ targetMappingsToResources.put(mapping, traversals);
+ newResources.addAll(internalGetResources(traversals));
+ }
+ }
+ return newResources;
+ }
+
+ private Collection internalGetResources(ResourceTraversal[] traversals) {
+ Set result = new HashSet();
+ for (int i = 0; i < traversals.length; i++) {
+ ResourceTraversal traversal = traversals[i];
+ IResource[] resources = traversal.getResources();
+ for (int j = 0; j < resources.length; j++) {
+ IResource resource = resources[j];
+ //TODO: should we check for parent/child relationships?
+ result.add(resource);
+ }
+ }
+ return result;
+ }
+
+ private void buildInputMappingToResourcesMap(IProgressMonitor monitor) throws CoreException {
+ ResourceMapping[] inputMappings = getSeedMappings();
+ monitor.beginTask(null, inputMappings.length * 100);
+ for (int i = 0; i < inputMappings.length; i++) {
+ ResourceMapping mapping = inputMappings[i];
+ ResourceTraversal[] traversals = mapping.getTraversals(getContext(), Policy.subMonitorFor(monitor, 100));
+ inputMappingsToResources.put(mapping, traversals);
+ }
+ monitor.done();
+ }
+
+ public ResourceMapping[] getInputMappings() {
+ return (ResourceMapping[]) targetMappingsToResources.keySet().toArray(new ResourceMapping[targetMappingsToResources.size()]);
+ }
+
+ public ResourceTraversal[] getInputTraversals() {
+ Collection values = targetMappingsToResources.values();
+ List result = new ArrayList();
+ for (Iterator iter = values.iterator(); iter.hasNext();) {
+ ResourceTraversal[] traversals = (ResourceTraversal[]) iter.next();
+ for (int i = 0; i < traversals.length; i++) {
+ ResourceTraversal traversal = traversals[i];
+ result.add(traversal);
+ }
+ }
+ return combineTraversals((ResourceTraversal[]) result.toArray(new ResourceTraversal[result.size()]));
+ }
+
+ public ResourceTraversal[] getTraversal(ResourceMapping mapping) {
+ return (ResourceTraversal[])targetMappingsToResources.get(mapping);
+ }
+
+ public boolean hasAdditionalMappings() {
+ return hasAdditionalMappings;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/SynchronizeOperationContext.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/SynchronizeOperationContext.java
new file mode 100644
index 000000000..00577d4e0
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/SynchronizeOperationContext.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.util.ListenerList;
+import org.eclipse.jface.util.SafeRunnable;
+
+/**
+ * A synchronize operation context that supports caching of
+ * properties relevant to the operation and the registering of
+ * dispose listeners.
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @see org.eclipse.team.ui.mapping.ISynchronizeOperationContext
+ * @since 3.2
+ */
+public abstract class SynchronizeOperationContext extends TeamViewerContext implements ISynchronizeOperationContext {
+
+ Map properties;
+ ListenerList listeners;
+ private final IResourceMappingOperationInput input;
+
+ /**
+ * Create an operation context for the given input.
+ * @param input the input of the context
+ */
+ public SynchronizeOperationContext(IResourceMappingOperationInput input) {
+ super(input.getInputMappings());
+ this.input = input;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.mapping.ISynchronizeOperationContext#addProperty(java.lang.String, java.lang.Object)
+ */
+ public synchronized void addProperty(String name, Object value) {
+ if (properties == null) {
+ properties = new HashMap();
+ }
+ properties.put(name, value);
+ }
+
+ public synchronized Object getProperty(String name) {
+ if (properties == null)
+ return null;
+ return properties.get(name);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.mapping.ISynchronizeOperationContext#removeProperty(java.lang.String)
+ */
+ public synchronized void removeProperty(String name) {
+ if (properties != null)
+ properties.remove(name);
+ if (properties.isEmpty()) {
+ properties = null;
+ }
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.mapping.ISynchronizeOperationContext#addDisposeListener(org.eclipse.team.ui.mapping.IDisposeListener)
+ */
+ public synchronized void addDisposeListener(IDisposeListener listener) {
+ if (listeners == null)
+ listeners = new ListenerList();
+ listeners.add(listener);
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.mapping.ISynchronizeOperationContext#removeDisposeListener(org.eclipse.team.ui.mapping.IDisposeListener)
+ */
+ public synchronized void removeDisposeListener(IDisposeListener listener) {
+ if (listeners != null)
+ listeners.remove(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.mapping.ISynchronizationContext#dispose()
+ */
+ public void dispose() {
+ if (listeners != null) {
+ Object[] allListeners = listeners.getListeners();
+ for (int i = 0; i < allListeners.length; i++) {
+ final Object listener = allListeners[i];
+ Platform.run(new SafeRunnable(){
+ public void run() throws Exception {
+ ((IDisposeListener)listener).contextDisposed(SynchronizeOperationContext.this);
+ }
+ });
+ }
+ }
+ properties = null;
+ }
+
+ /**
+ * Return the input used to create this operation context.
+ * @return the input used to create this operation context
+ */
+ public IResourceMappingOperationInput getInput() {
+ return input;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/TeamViewerContext.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/TeamViewerContext.java
new file mode 100644
index 000000000..b7be918e8
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/mapping/TeamViewerContext.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.ui.mapping;
+
+import java.util.*;
+
+import org.eclipse.core.resources.mapping.ModelProvider;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+
+/**
+ * A concrete implementation of the <code>ITeamViewerContext</code>
+ *
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ *
+ * @since 3.2
+ */
+public class TeamViewerContext implements ITeamViewerContext {
+
+ private final ResourceMapping[] mappings;
+
+ public TeamViewerContext(ResourceMapping[] mappings) {
+ this.mappings = mappings;
+ }
+
+ public ResourceMapping[] getResourceMappings(String modelProviderId) {
+ if (modelProviderId.equals(ALL_MAPPINGS)) {
+ return mappings;
+ }
+ List result = new ArrayList();
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ if (mapping.getModelProviderId().equals(modelProviderId)) {
+ result.add(mapping);
+ }
+ }
+ return (ResourceMapping[]) result.toArray(new ResourceMapping[result.size()]);
+ }
+
+ public ModelProvider[] getModelProviders() {
+ Set providers = new HashSet();
+ for (int i = 0; i < mappings.length; i++) {
+ ResourceMapping mapping = mappings[i];
+ providers.add(mapping.getModelProvider());
+ }
+ return (ModelProvider[]) providers.toArray(new ModelProvider[providers.size()]);
+ }
+
+}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/AbstractSynchronizeScope.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/AbstractSynchronizeScope.java
index cbc3d0c03..e00bd5398 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/AbstractSynchronizeScope.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/AbstractSynchronizeScope.java
@@ -10,7 +10,12 @@
*******************************************************************************/
package org.eclipse.team.ui.synchronize;
+import java.util.ArrayList;
+import java.util.List;
+
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.ResourceMapping;
+import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.ListenerList;
@@ -163,4 +168,29 @@ public abstract class AbstractSynchronizeScope implements ISynchronizeScope {
protected void init(IMemento memento) {
// Do nothing by default
}
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.ui.synchronize.ISynchronizeScope#contains(org.eclipse.core.resources.IResource)
+ */
+ public boolean contains(IResource resource) {
+ IResource[] roots = getRoots();
+ IPath resourcePath = resource.getFullPath();
+ for (int i = 0; i < roots.length; i++) {
+ IResource root = roots[i];
+ if (root.getFullPath().isPrefixOf(resourcePath)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ResourceMapping[] getResourceMappings() {
+ List result = new ArrayList();
+ IResource[] roots = getRoots();
+ for (int i = 0; i < roots.length; i++) {
+ IResource resource = roots[i];
+ result.add(resource.getAdapter(ResourceMapping.class));
+ }
+ return (ResourceMapping[]) result.toArray(new ResourceMapping[result.size()]);
+ }
}
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/ISynchronizeScope.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/ISynchronizeScope.java
index 0ade028e1..7a1961388 100644
--- a/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/ISynchronizeScope.java
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/ui/synchronize/ISynchronizeScope.java
@@ -11,6 +11,7 @@
package org.eclipse.team.ui.synchronize;
import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.mapping.ResourceMapping;
import org.eclipse.jface.util.IPropertyChangeListener;
/**
@@ -36,6 +37,14 @@ public interface ISynchronizeScope {
public static final String NAME = "prop_name"; //$NON-NLS-1$
/**
+ * Property used to indicate that the roots of the scope have
+ * not changes but the resources covered by the scope have.
+ * @see ISynchronizeScope#contains(IResource)
+ * @since 3.2
+ */
+ public static final String CONTAINMENT = "prop_containment"; //$NON-NLS-1$
+
+ /**
* Return the name of the scope
*
* @return the name of the scope
@@ -71,4 +80,20 @@ public interface ISynchronizeScope {
* Dispose of the scope when it is no longer needed.
*/
public void dispose();
+
+ /**
+ * Return whether the given resource is contained in this scope.
+ * @param resource the resource to be tested
+ * @return whether the given resource is contained in this scope
+ *
+ * @since 3.2
+ */
+ public boolean contains(IResource resource);
+
+ /**
+ *
+ * @return
+ * @since 3.2
+ */
+ public ResourceMapping[] getResourceMappings();
}
diff --git a/tests/org.eclipse.team.tests.cvs.core/launchConfigurations/CVS UI Tests.launch b/tests/org.eclipse.team.tests.cvs.core/launchConfigurations/CVS UI Tests.launch
index a91334527..67cb9dc59 100644
--- a/tests/org.eclipse.team.tests.cvs.core/launchConfigurations/CVS UI Tests.launch
+++ b/tests/org.eclipse.team.tests.cvs.core/launchConfigurations/CVS UI Tests.launch
@@ -55,19 +55,19 @@
<mapEntry key="org.eclipse.help.ui/debug/ieadapter/inprocess" value="false"/>
<mapEntry key="org.eclipse.jface/trace/actions" value="false"/>
<mapEntry key="org.eclipse.core.runtime/registry/debug/events/extension" value="false"/>
-<mapEntry key="org.eclipse.help.webapp/debug" value="true"/>
<mapEntry key="org.eclipse.jdt.core/debug/hierarchy" value="false"/>
+<mapEntry key="org.eclipse.help.webapp/debug" value="true"/>
<mapEntry key="org.eclipse.team.cvs.core/debug" value="true"/>
<mapEntry key="org.eclipse.jdt.core/debug/cpresolution" value="false"/>
<mapEntry key="org.eclipse.team.cvs.core/threading" value="false"/>
<mapEntry key="org.eclipse.update.core/debug/warning" value="false"/>
<mapEntry key="org.eclipse.core.resources/save" value="false"/>
-<mapEntry key="org.eclipse.ui/debug/internalerror/openDialog" value="false"/>
<mapEntry key="org.eclipse.update.core/debug/install" value="false"/>
+<mapEntry key="org.eclipse.ui/debug/internalerror/openDialog" value="false"/>
<mapEntry key="org.eclipse.core.resources/debug" value="false"/>
<mapEntry key="org.eclipse.update.core/debug/parsing" value="false"/>
-<mapEntry key="org.eclipse.core.boot/trace/filters" value="trace.properties"/>
<mapEntry key="org.eclipse.core.resources/restore/syncinfo" value="false"/>
+<mapEntry key="org.eclipse.core.boot/trace/filters" value="trace.properties"/>
<mapEntry key="org.eclipse.team.cvs.core/cvsprotocol" value="true"/>
<mapEntry key="org.eclipse.jdt.core/debug/postaction" value="false"/>
<mapEntry key="org.eclipse.core.runtime/url/debug/cachelookup" value="false"/>
@@ -77,15 +77,15 @@
<mapEntry key="org.eclipse.core.resources/save/syncinfo" value="false"/>
<mapEntry key="org.eclipse.core.runtime/loader/debug/filter/resource" value="*"/>
<mapEntry key="org.eclipse.team.ftp/list" value="true"/>
-<mapEntry key="org.eclipse.team.cvs.ssh/debug" value="false"/>
<mapEntry key="org.eclipse.core.runtime/loader/debug/activateplugin" value="false"/>
+<mapEntry key="org.eclipse.team.cvs.ssh/debug" value="false"/>
<mapEntry key="org.eclipse.core.resources/monitor/builders" value="false"/>
<mapEntry key="org.eclipse.core.resources/save/markers" value="false"/>
<mapEntry key="org.eclipse.update.core/debug/installhandler" value="false"/>
-<mapEntry key="org.eclipse.jdt.core/debug/selection" value="false"/>
<mapEntry key="org.eclipse.jdt.core/debug/javadelta" value="false"/>
-<mapEntry key="org.eclipse.ui/trace/workbench.restore" value="false"/>
+<mapEntry key="org.eclipse.jdt.core/debug/selection" value="false"/>
<mapEntry key="org.eclipse.core.resources/restore/tree" value="false"/>
+<mapEntry key="org.eclipse.ui/trace/workbench.restore" value="false"/>
<mapEntry key="org.eclipse.ui/trace/part.create" value="false"/>
<mapEntry key="org.eclipse.help/debug/protocols" value="false"/>
<mapEntry key="org.eclipse.core.resources/restore/snapshots" value="false"/>
@@ -93,13 +93,13 @@
<mapEntry key="org.eclipse.team.cvs.core/dirtycaching" value="false"/>
<mapEntry key="org.eclipse.core.runtime/loader/debug/filter/native" value="*"/>
<mapEntry key="org.eclipse.jdt.core/debug/zipaccess" value="false"/>
-<mapEntry key="org.eclipse.core.runtime/loader/debug/properties" value="false"/>
<mapEntry key="org.eclipse.core.runtime/url/debug/cachecopy" value="false"/>
+<mapEntry key="org.eclipse.core.runtime/loader/debug/properties" value="false"/>
<mapEntry key="org.eclipse.core.runtime/loader/debug/create" value="false"/>
<mapEntry key="org.eclipse.core.runtime/jobs/beginend" value="false"/>
<mapEntry key="org.eclipse.team.cvs.core/metafiles" value="false"/>
-<mapEntry key="org.eclipse.team.core/debug" value="false"/>
<mapEntry key="org.eclipse.team.ftp/responses" value="true"/>
+<mapEntry key="org.eclipse.team.core/debug" value="false"/>
<mapEntry key="org.eclipse.core.runtime/loader/debug/prefixes/success" value="false"/>
<mapEntry key="org.eclipse.core.runtime/jobs" value="false"/>
<mapEntry key="org.eclipse.core.boot/monitor/plugins" value="false"/>
@@ -125,8 +125,8 @@
<mapEntry key="org.eclipse.core.runtime/config/debug" value="false"/>
<mapEntry key="org.eclipse.help.ui/debug" value="true"/>
<mapEntry key="org.eclipse.core.resources/restore/metainfo" value="false"/>
-<mapEntry key="org.eclipse.debug.ui/debug" value="true"/>
<mapEntry key="org.eclipse.core.resources/save/mastertable" value="false"/>
+<mapEntry key="org.eclipse.debug.ui/debug" value="true"/>
<mapEntry key="org.eclipse.jdt.debug/debug" value="true"/>
<mapEntry key="org.eclipse.core.runtime/debug" value="false"/>
<mapEntry key="org.eclipse.jdt.core/debug/buffermanager" value="false"/>
@@ -142,17 +142,18 @@
<booleanAttribute key="automaticAdd" value="true"/>
<stringAttribute key="checked" value="[NONE]"/>
<booleanAttribute key="includeFragments" value="false"/>
+<stringAttribute key="location" value="C:\Eclipse\Latest-Eclipse-Drop\eclipse\runtime-test-workspace"/>
<booleanAttribute key="clearws" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.team.tests.ccvs.core.AllTests"/>
<stringAttribute key="location1" value="C:\Eclipse\Latest-Eclipse-Drop\eclipse\runtime-test-workspace"/>
-<stringAttribute key="vmargs" value="-Declipse.cvs.properties=c:\eclipse\repository.properties -Declipse.cvs.testName2=testFileAdditions"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os win32 -ws win32 -arch x86 -nl en_CA"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Declipse.cvs.properties=c:\eclipse\repository.properties -Declipse.cvs.testName2=testFileAdditions"/>
<booleanAttribute key="default" value="true"/>
<booleanAttribute key="clearConfig" value="true"/>
<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
-<booleanAttribute key="useDefaultConfigArea" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+<booleanAttribute key="useDefaultConfigArea" value="true"/>
<stringAttribute key="onePluginID" value=""/>
-<stringAttribute key="progargs" value="-os win32 -ws win32 -arch x86 -nl en_CA"/>
<booleanAttribute key="useDefaultConfig" value="true"/>
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.team.tests.cvs.core"/>
@@ -160,5 +161,4 @@
<booleanAttribute key="onePlugin" value="false"/>
<booleanAttribute key="includeOptional" value="true"/>
<booleanAttribute key="maximized" value="false"/>
-<stringAttribute key="location0" value="C:\Eclipse\Latest-Eclipse-Drop\eclipse\runtime-test-workspace"/>
</launchConfiguration>
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/EclipseTest.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/EclipseTest.java
index 3df328a32..1c9a3927d 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/EclipseTest.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/EclipseTest.java
@@ -833,15 +833,19 @@ public class EclipseTest extends ResourceTest {
}
protected void setContentsAndEnsureModified(IFile file) throws CoreException, TeamException {
- setContentsAndEnsureModified(file, getRandomContents().toString());
+ setContentsAndEnsureModified(file, getRandomContents());
}
protected void setContentsAndEnsureModified(IFile file, String contents) throws CoreException, CVSException {
+ if (contents == null) contents ="";
+ setContentsAndEnsureModified(file, new ByteArrayInputStream(contents.getBytes()));
+ }
+
+ protected void setContentsAndEnsureModified(IFile file, InputStream stream) throws CoreException, CVSException {
ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(file);
int count = 0;
- if (contents == null) contents ="";
do {
- file.setContents(new ByteArrayInputStream(contents.getBytes()), false, false, null);
+ file.setContents(stream, false, false, null);
assertTrue("Timestamp granularity is too small. Increase test wait factor", count <= CVSTestSetup.WAIT_FACTOR);
if (!cvsFile.isModified(null)) {
waitMsec(1500);
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/ResourceMapperTests.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/ResourceMapperTests.java
index f28c6da4e..ff7fd6fa9 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/ResourceMapperTests.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/ResourceMapperTests.java
@@ -11,21 +11,30 @@
package org.eclipse.team.tests.ccvs.core.mappings;
import java.io.IOException;
+import java.io.InputStream;
import java.util.*;
import junit.framework.Test;
+import org.eclipse.core.internal.resources.mapping.SimpleResourceMapping;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.mapping.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.synchronize.*;
+import org.eclipse.team.core.variants.CachedResourceVariant;
+import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFolderTree;
import org.eclipse.team.internal.ccvs.core.resources.RemoteFolderTreeBuilder;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
+import org.eclipse.team.internal.ccvs.ui.operations.CacheBaseContentsOperation;
+import org.eclipse.team.internal.ccvs.ui.operations.CacheRemoteContentsOperation;
+import org.eclipse.team.internal.core.ResourceVariantCache;
+import org.eclipse.team.internal.core.ResourceVariantCacheEntry;
import org.eclipse.team.tests.ccvs.core.EclipseTest;
/**
@@ -276,6 +285,9 @@ public class ResourceMapperTests extends EclipseTest {
new ResourceTraversal(resources, depth, IResource.NONE)
};
}
+ public String getModelProviderId() {
+ return ModelProvider.RESOURCE_MODEL_PROVIDER_ID;
+ }
};
}
@@ -531,4 +543,112 @@ public class ResourceMapperTests extends EclipseTest {
add(asResourceMapping(new IResource[] { project }, IResource.DEPTH_INFINITE));
}
+ public void testCacheBase() throws TeamException, CoreException {
+ IProject project = createProject("testCacheBase", new String[] { "changed.txt", "deleted.txt", "folder1/", "folder1/a.txt", "folder1/b.txt", "folder1/subfolder1/c.txt" });
+ IProject copy = checkoutCopy(project, "-copy");
+
+ // First, make some local changes and then cache the bases
+ setContentsAndEnsureModified(project.getFile("changed.txt"), "Uncomited text");
+ setContentsAndEnsureModified(project.getFile("folder1/b.txt"));
+ project.getFile("deleted.txt").delete(false, true, null);
+ cacheBase(project, true /* cache for outgoing and conflicting */);
+ cacheBase(project, false /* cache for conflicting only*/);
+
+ // Next, retry after releasing some changes (to ensure proper contents are fetched)
+ setContentsAndEnsureModified(copy.getFile("changed.txt"), "Text comited from the copy");
+ commitProject(copy);
+ cacheBase(project, true /* cache for outgoing and conflicting */);
+ cacheBase(project, false /* cache for conflicting only */);
+ }
+
+ public void testCacheRemote() throws TeamException, CoreException {
+ IProject project = createProject("testCacheRemote", new String[] { "changed.txt", "deleted.txt", "folder1/", "folder1/a.txt", "folder1/b.txt", "folder1/subfolder1/c.txt" });
+ IProject copy = checkoutCopy(project, "-copy");
+
+ // Make some remote changes
+ setContentsAndEnsureModified(copy.getFile("changed.txt"), "Uncomited text");
+ setContentsAndEnsureModified(copy.getFile("folder1/b.txt"));
+ commitProject(copy);
+ // Delete a local file
+ project.getFile("deleted.txt").delete(false, true, null);
+ cacheRemote(project);
+ }
+
+ private void cacheRemote(IProject project) throws TeamException {
+ clearCache(project);
+ CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber().refresh(new IProject[] { project }, IResource.DEPTH_INFINITE, DEFAULT_MONITOR);
+ SyncInfoTree tree = getAllOutOfSync(new IProject[] { project });
+ ResourceMapping[] mappings = new ResourceMapping[] {new SimpleResourceMapping(project)};
+ CacheRemoteContentsOperation op = new CacheRemoteContentsOperation(null, mappings, tree);
+ executeHeadless(op);
+ ensureRemoteCached(tree);
+ }
+
+ private void cacheBase(IProject project, boolean includeOutgoing) throws CoreException {
+ clearCache(project);
+ CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber().refresh(new IProject[] { project }, IResource.DEPTH_INFINITE, DEFAULT_MONITOR);
+ SyncInfoTree tree = getAllOutOfSync(new IProject[] { project });
+ ResourceMapping[] mappings = new ResourceMapping[] {new SimpleResourceMapping(project)};
+ CacheBaseContentsOperation op = new CacheBaseContentsOperation(null, mappings, tree, includeOutgoing);
+ executeHeadless(op);
+ ensureBaseCached(tree, includeOutgoing);
+ }
+
+ private void ensureRemoteCached(SyncInfoTree tree) {
+ for (Iterator iter = tree.iterator(); iter.hasNext();) {
+ SyncInfo info = (SyncInfo) iter.next();
+ IResourceVariant remote = info.getRemote();
+ if (remote != null) {
+ boolean isCached = ((CachedResourceVariant)remote).isContentsCached();
+ int direction = SyncInfo.getDirection(info.getKind());
+ if (direction == SyncInfo.CONFLICTING || direction == SyncInfo.INCOMING) {
+ assertTrue(NLS.bind("The remote contents should be cached for {0}", new String[] {info.getLocal().getFullPath().toString()}), isCached);
+ } else {
+ assertFalse(NLS.bind("The base contents should NOT be cached for {0}", new String[] {info.getLocal().getFullPath().toString()}), isCached);
+ }
+ }
+ }
+ }
+
+ private void ensureBaseCached(SyncInfoTree tree, boolean includeOutgoing) throws TeamException, CoreException {
+ for (Iterator iter = tree.iterator(); iter.hasNext();) {
+ SyncInfo info = (SyncInfo) iter.next();
+ IResourceVariant base = info.getBase();
+ if (base != null) {
+ boolean isCached = ((CachedResourceVariant)base).isContentsCached();
+ int direction = SyncInfo.getDirection(info.getKind());
+ if (direction == SyncInfo.CONFLICTING || (includeOutgoing && direction == SyncInfo.OUTGOING)) {
+ assertTrue(NLS.bind("The base contents should be cached for {0}", new String[] {info.getLocal().getFullPath().toString()}), isCached);
+ // For conflicts, ensure that the cache contents do not match the remote
+ if (direction == SyncInfo.CONFLICTING) {
+ IResourceVariant remote = info.getRemote();
+ if (remote != null) {
+ InputStream baseIn = base.getStorage(DEFAULT_MONITOR).getContents();
+ if (baseIn == null) {
+ fail(NLS.bind("Base was not fetched for for {0}", new String[] {info.getLocal().getFullPath().toString()}));
+ }
+ InputStream remoteIn = remote.getStorage(DEFAULT_MONITOR).getContents();
+ if (compareContent(baseIn, remoteIn)) {
+ fail(NLS.bind("The remote was fetched instead of the base for {0}", new String[] {info.getLocal().getFullPath().toString()}));
+ }
+ }
+ }
+ } else {
+ assertFalse(NLS.bind("The base contents should NOT be cached for {0}", new String[] {info.getLocal().getFullPath().toString()}), isCached);
+ }
+ }
+ }
+ }
+
+ private void clearCache(IProject project) {
+ ResourceVariantCache cache = ResourceVariantCache.getCache(CVSProviderPlugin.ID);
+ if (cache != null) {
+ ResourceVariantCacheEntry[] entries = cache.getEntries();
+ for (int i = 0; i < entries.length; i++) {
+ ResourceVariantCacheEntry entry = entries[i];
+ entry.dispose();
+ }
+ }
+ }
+
}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/SyncInfoSetTraveralContext.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/SyncInfoSetTraveralContext.java
index ad14b21ed..9eec8540c 100644
--- a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/SyncInfoSetTraveralContext.java
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/mappings/SyncInfoSetTraveralContext.java
@@ -48,11 +48,10 @@ public class SyncInfoSetTraveralContext extends RemoteResourceMappingContext {
/* (non-Javadoc)
* @see org.eclipse.core.resources.mapping.ITraversalContext#fetchContents(org.eclipse.core.resources.IFile, org.eclipse.core.runtime.IProgressMonitor)
*/
- public IStorage fetchContents(IFile file, IProgressMonitor monitor) throws CoreException {
+ public IStorage fetchRemoteContents(IFile file, IProgressMonitor monitor) throws CoreException {
SyncInfo info = getSyncInfo(file);
- //TODO: Speced to throw an exception when remote doesn't exist
if (info == null)
- return file;
+ return null;
IResourceVariant remote = info.getRemote();
if (remote == null)
return null;
@@ -73,4 +72,35 @@ public class SyncInfoSetTraveralContext extends RemoteResourceMappingContext {
// Do nothing
}
+ public boolean isThreeWay() {
+ for (Iterator iter = set.iterator(); iter.hasNext();) {
+ SyncInfo info = (SyncInfo) iter.next();
+ return info.getComparator().isThreeWay();
+ }
+ return true;
+ }
+
+ public boolean hasRemoteChange(IResource resource, IProgressMonitor monitor) throws CoreException {
+ SyncInfo info = set.getSyncInfo(resource);
+ int direction = SyncInfo.getDirection(info.getKind());
+ return direction == SyncInfo.INCOMING || direction == SyncInfo.CONFLICTING;
+ }
+
+ public boolean hasLocalChange(IResource resource, IProgressMonitor monitor) throws CoreException {
+ SyncInfo info = set.getSyncInfo(resource);
+ int direction = SyncInfo.getDirection(info.getKind());
+ return direction == SyncInfo.OUTGOING || direction == SyncInfo.CONFLICTING;
+
+ }
+
+ public IStorage fetchBaseContents(IFile file, IProgressMonitor monitor) throws CoreException {
+ SyncInfo info = getSyncInfo(file);
+ if (info == null)
+ return null;
+ IResourceVariant base = info.getBase();
+ if (base == null)
+ return null;
+ return base.getStorage(monitor);
+ }
+
}

Back to the top