Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcvs2svn2004-12-02 12:31:50 +0000
committercvs2svn2004-12-02 12:31:50 +0000
commit4abda86bad64803c35f06903d34108db63f05739 (patch)
treedb57d36b53e75a6dc885c0420d08063f3018cf2a
parentbce7ada93c6744ce6d44fb5619cae03190a61eb5 (diff)
downloadeclipse.platform.team-4abda86bad64803c35f06903d34108db63f05739.tar.gz
eclipse.platform.team-4abda86bad64803c35f06903d34108db63f05739.tar.xz
eclipse.platform.team-4abda86bad64803c35f06903d34108db63f05739.zip
This commit was manufactured by cvs2svn to create branch
'branch_traversals_20040920'. Cherrypick from master 2004-12-02 12:31:48 UTC Michael Valenta <mvalenta> 'Bug 78989 Unfriendly message when working disconnected': bundles/org.eclipse.team.cvs.core/schema/connectionmethods.exsd bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSCoreFileModificationValidator.java bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Add.java bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/CommandOutputListener.java bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/AdminKSubstListener.java bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/LogListener.java bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/UpdateListener.java bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/BranchPromptDialog.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFileElement.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFolderElement.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/FilteredTagList.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/LocalProjectTagSource.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/MultiFolderTagSource.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFileTagSource.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFolderTagSource.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagAsVersionDialog.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagConfigurationDialog.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagContentAssistProcessor.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagElement.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRefreshButtonArea.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRootElement.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionArea.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionDialog.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionWizardPage.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSource.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceResourceAdapter.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceWorkbenchAdapter.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizard.java bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizardPage.java tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/EclipseTest.java tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSChangeSetTests.java tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/SyncInfoSource.java tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/SynchronizeViewTestAdapter.java
-rw-r--r--bundles/org.eclipse.team.cvs.core/schema/connectionmethods.exsd146
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSCoreFileModificationValidator.java177
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java1013
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Add.java100
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/CommandOutputListener.java71
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/AdminKSubstListener.java125
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/LogListener.java226
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/UpdateListener.java293
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties351
-rw-r--r--bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java106
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/BranchPromptDialog.java231
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFileElement.java75
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFolderElement.java82
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/FilteredTagList.java108
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/LocalProjectTagSource.java136
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/MultiFolderTagSource.java53
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFileTagSource.java98
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFolderTagSource.java142
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagAsVersionDialog.java182
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagConfigurationDialog.java763
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagContentAssistProcessor.java160
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagElement.java89
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRefreshButtonArea.java175
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRootElement.java75
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionArea.java612
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionDialog.java247
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionWizardPage.java181
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSource.java234
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceResourceAdapter.java88
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceWorkbenchAdapter.java157
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizard.java95
-rw-r--r--bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizardPage.java254
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/EclipseTest.java1010
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSChangeSetTests.java419
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/SyncInfoSource.java107
-rw-r--r--tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/SynchronizeViewTestAdapter.java338
36 files changed, 8719 insertions, 0 deletions
diff --git a/bundles/org.eclipse.team.cvs.core/schema/connectionmethods.exsd b/bundles/org.eclipse.team.cvs.core/schema/connectionmethods.exsd
new file mode 100644
index 000000000..0faa3b477
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/schema/connectionmethods.exsd
@@ -0,0 +1,146 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.eclipse.team.cvs.core">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.eclipse.team.cvs.core" id="connectionmethods" name="ConnectionMethods"/>
+ </appInfo>
+ <documentation>
+ This extension point allows additional CVS connection methods to be pluged in. It is for internal use.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <complexType>
+ <sequence>
+ <element ref="adapter"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="adapter">
+ <complexType>
+ <sequence>
+ <element ref="run"/>
+ </sequence>
+ </complexType>
+ </element>
+
+ <element name="run">
+ <complexType>
+ <sequence>
+ <element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
+ </sequence>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+ An implementation of &lt;samp&gt;IConnectionMethod&lt;/samp&gt;
+ </documentation>
+ <appInfo>
+ <meta.attribute kind="java"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="parameter">
+ <complexType>
+ <attribute name="name" type="string" use="required">
+ <annotation>
+ <documentation>
+ This extension point is internal use only
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="value" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 1.0
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ Following is an example of a connectionmethods extension:
+
+&lt;p&gt;
+&lt;pre&gt;
+ &lt;extension id=&quot;pserver&quot; point=&quot;org.eclipse.team.cvs.core.connectionmethods&quot;&gt;
+ &lt;adapter&gt;
+ &lt;run class=&quot;org.eclipse.team.internal.ccvs.core.connection.PServerConnectionMethod&quot;&gt;
+ &lt;parameter name=&quot;trace&quot; value=&quot;false&quot;&gt;
+ &lt;/parameter&gt;
+ &lt;/run&gt;
+ &lt;/adapter&gt;
+ &lt;/extension&gt;
+&lt;/pre&gt;
+&lt;/p&gt;
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiInfo"/>
+ </appInfo>
+ <documentation>
+ This extension point is internal use only
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="copyright"/>
+ </appInfo>
+ <documentation>
+ Copyright (c) 2004 IBM Corporation and others.
+All rights reserved. This program and the accompanying materials are made available under the terms of the Common Public License v1.0 which accompanies this distribution, and is available at &lt;a href=&quot;http://www.eclipse.org/legal/cpl-v10.html&quot;&gt;http://www.eclipse.org/legal/cpl-v10.html&lt;/a&gt;
+ </documentation>
+ </annotation>
+
+</schema>
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSCoreFileModificationValidator.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSCoreFileModificationValidator.java
new file mode 100644
index 000000000..b7dd96b7b
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSCoreFileModificationValidator.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+
+/**
+ * Core validator that will load the UI validator only if a prompt is needed
+ */
+public class CVSCoreFileModificationValidator implements ICVSFileModificationValidator {
+
+ IFileModificationValidator uiValidator;
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IFileModificationValidator#validateEdit(org.eclipse.core.resources.IFile[], java.lang.Object)
+ */
+ public IStatus validateEdit(IFile[] files, Object context) {
+ IFile[] unmanagedReadOnlyFiles = getUnmanagedReadOnlyFiles(files);
+ if (unmanagedReadOnlyFiles.length > 0) {
+ IStatus status = setWritable(unmanagedReadOnlyFiles);
+ if (!status.isOK()) {
+ return status;
+ }
+ }
+ IFile[] readOnlyFiles = getManagedReadOnlyFiles(files);
+ if (readOnlyFiles.length == 0) return Status.OK_STATUS;
+ return edit(readOnlyFiles, context);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IFileModificationValidator#validateSave(org.eclipse.core.resources.IFile)
+ */
+ public IStatus validateSave(IFile file) {
+ if (!needsCheckout(file)) {
+ if (file.isReadOnly()) {
+ setWritable(new IFile[] { file } );
+ }
+ return Status.OK_STATUS;
+ }
+ return edit(new IFile[] {file}, (Object)null);
+ }
+
+ /**
+ * Method for editing a set of files. Is overriden by the
+ * UI to prompt the user. Default behavior is to try and load the
+ * UI validator and, failing that, to edit without
+ * prompting.
+ * @param readOnlyFiles
+ * @param context
+ * @return
+ */
+ protected IStatus edit(IFile[] readOnlyFiles, Object context) {
+ synchronized(this) {
+ if (uiValidator == null) {
+ uiValidator = getPluggedInValidator();
+ }
+ }
+ if (uiValidator != null) {
+ return uiValidator.validateEdit(readOnlyFiles, context);
+ } else {
+ try {
+ performEdit(readOnlyFiles, new NullProgressMonitor());
+ } catch (CVSException e) {
+ return e.getStatus();
+ }
+ return Status.OK_STATUS;
+ }
+ }
+ /**
+ * @see org.eclipse.team.internal.ccvs.core.ICVSFileModificationValidator#validateMoveDelete(org.eclipse.core.resources.IFile[], org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus validateMoveDelete(IFile[] files, IProgressMonitor monitor) {
+ IFile[] readOnlyFiles = getManagedReadOnlyFiles(files);
+ if (readOnlyFiles.length == 0) return Status.OK_STATUS;
+
+ try {
+ performEdit(readOnlyFiles, monitor);
+ return Status.OK_STATUS;
+ } catch (CVSException e) {
+ return e.getStatus();
+ }
+ }
+
+ protected CVSTeamProvider getProvider(IFile[] files) {
+ CVSTeamProvider provider = (CVSTeamProvider)RepositoryProvider.getProvider(files[0].getProject(), CVSProviderPlugin.getTypeId());
+ return provider;
+ }
+
+ protected void performEdit(IFile[] files, IProgressMonitor monitor) throws CVSException {
+ getProvider(files).edit(files, false /* recurse */, true /* notify server */, ICVSFile.NO_NOTIFICATION, monitor);
+ }
+
+ private boolean needsCheckout(IFile file) {
+ try {
+ if (file.isReadOnly()) {
+ ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(file);
+ boolean managed = cvsFile.isManaged();
+ return managed;
+ }
+ } catch (CVSException e) {
+ // Log the exception and assume we don't need a checkout
+ CVSProviderPlugin.log(e);
+ }
+ return false;
+ }
+
+ protected IStatus setWritable(final IFile[] files) {
+ for (int i = 0; i < files.length; i++) {
+ IFile file = files[i];
+ ResourceAttributes attributes = file.getResourceAttributes();
+ if (attributes != null) {
+ attributes.setReadOnly(false);
+ }
+ try {
+ file.setResourceAttributes(attributes);
+ } catch (CoreException e) {
+ return CVSException.wrapException(e).getStatus();
+ }
+ }
+ return Status.OK_STATUS;
+ }
+
+ private IFile[] getManagedReadOnlyFiles(IFile[] files) {
+ List readOnlys = new ArrayList();
+ for (int i = 0; i < files.length; i++) {
+ IFile iFile = files[i];
+ if (needsCheckout(iFile)) {
+ readOnlys.add(iFile);
+ }
+ }
+ return (IFile[]) readOnlys.toArray(new IFile[readOnlys.size()]);
+ }
+
+ protected IFile[] getUnmanagedReadOnlyFiles(IFile[] files) {
+ List readOnlys = new ArrayList();
+ for (int i = 0; i < files.length; i++) {
+ IFile iFile = files[i];
+ if (iFile.isReadOnly() && !needsCheckout(iFile)) {
+ readOnlys.add(iFile);
+ }
+ }
+ return (IFile[]) readOnlys.toArray(new IFile[readOnlys.size()]);
+ }
+
+ private static IFileModificationValidator getPluggedInValidator() {
+ IExtension[] extensions = Platform.getExtensionRegistry().getExtensionPoint(CVSProviderPlugin.ID, CVSProviderPlugin.PT_FILE_MODIFICATION_VALIDATOR).getExtensions();
+ if (extensions.length == 0)
+ return null;
+ IExtension extension = extensions[0];
+ IConfigurationElement[] configs = extension.getConfigurationElements();
+ if (configs.length == 0) {
+ CVSProviderPlugin.log(IStatus.ERROR, Policy.bind("CVSAdapter.noConfigurationElement", new Object[] {extension.getUniqueIdentifier()}), null);//$NON-NLS-1$
+ return null;
+ }
+ try {
+ IConfigurationElement config = configs[0];
+ return (IFileModificationValidator) config.createExecutableExtension("run");//$NON-NLS-1$
+ } catch (CoreException ex) {
+ CVSProviderPlugin.log(IStatus.ERROR, Policy.bind("CVSAdapter.unableToInstantiate", new Object[] {extension.getUniqueIdentifier()}), ex);//$NON-NLS-1$
+ return null;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
new file mode 100644
index 000000000..2763a5ed5
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/CVSTeamProvider.java
@@ -0,0 +1,1013 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.core;
+
+import java.io.*;
+import java.util.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.resources.team.IMoveDeleteHook;
+import org.eclipse.core.resources.team.ResourceRuleFactory;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.core.runtime.jobs.MultiRule;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.client.*;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
+import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
+import org.eclipse.team.internal.ccvs.core.client.listeners.*;
+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.syncinfo.*;
+import org.eclipse.team.internal.ccvs.core.util.*;
+import org.eclipse.team.internal.core.streams.CRLFtoLFInputStream;
+import org.eclipse.team.internal.core.streams.LFtoCRLFInputStream;
+
+/**
+ * This class acts as both the ITeamNature and the ITeamProvider instances
+ * required by the Team core.
+ *
+ * The current stat of this class and it's plugin is EXPERIMENTAL.
+ * As such, it is subject to change except in it's conformance to the
+ * TEAM API which it implements.
+ *
+ * Questions:
+ *
+ * How should a project/reource rename/move effect the provider?
+ *
+ * Currently we always update with -P. Is this OK?
+ * - A way to allow customizable options would be nice
+ *
+ * Is the -l option valid for commit and does it work properly for update and commit?
+ *
+ * Do we need an IUserInteractionProvider in the CVS core
+ * - prompt for user info (caching could be separate)
+ * - get release comments
+ * - prompt for overwrite of unmanaged files
+ *
+ * Need a mechanism for communicating meta-information (provided by Team?)
+ *
+ * Should pass null when there are no options for a cvs command
+ *
+ * We currently write the files to disk and do a refreshLocal to
+ * have them appear in Eclipse. This may be changed in the future.
+ */
+public class CVSTeamProvider extends RepositoryProvider {
+
+ private static final ResourceRuleFactory RESOURCE_RULE_FACTORY = new ResourceRuleFactory() {
+ public ISchedulingRule validateEditRule(IResource[] resources) {
+ if (resources.length == 0)
+ return null;
+ //optimize rule for single file
+ if (resources.length == 1)
+ return resources[0].isReadOnly() ? parent(resources[0]) : null;
+ //need a lock on the parents of all read-only files
+ HashSet rules = new HashSet();
+ for (int i = 0; i < resources.length; i++)
+ if (resources[i].isReadOnly())
+ rules.add(parent(resources[i]));
+ if (rules.isEmpty())
+ return null;
+ if (rules.size() == 1)
+ return (ISchedulingRule) rules.iterator().next();
+ ISchedulingRule[] ruleArray = (ISchedulingRule[]) rules
+ .toArray(new ISchedulingRule[rules.size()]);
+ return new MultiRule(ruleArray);
+ }
+ };
+
+ private static final boolean IS_CRLF_PLATFORM = Arrays.equals(
+ System.getProperty("line.separator").getBytes(), new byte[] { '\r', '\n' }); //$NON-NLS-1$
+
+ public static final IStatus OK = new Status(IStatus.OK, CVSProviderPlugin.ID, 0, Policy.bind("ok"), null); //$NON-NLS-1$
+
+ private static final int UNIFIED_FORMAT = 0;
+ private static final int CONTEXT_FORMAT = 1;
+ private static final int STANDARD_FORMAT = 2;
+
+ private CVSWorkspaceRoot workspaceRoot;
+ private IProject project;
+
+ private static MoveDeleteHook moveDeleteHook= new MoveDeleteHook();
+ private static IFileModificationValidator fileModificationValidator;
+
+ // property used to indicate whether new directories should be discovered for the project
+ private final static QualifiedName FETCH_ABSENT_DIRECTORIES_PROP_KEY =
+ new QualifiedName("org.eclipse.team.cvs.core", "fetch_absent_directories"); //$NON-NLS-1$ //$NON-NLS-2$
+ // property used to indicate whether the project is configured to use Watch/edit
+ private final static QualifiedName WATCH_EDIT_PROP_KEY =
+ new QualifiedName("org.eclipse.team.cvs.core", "watch_edit"); //$NON-NLS-1$ //$NON-NLS-2$
+
+ /**
+ * No-arg Constructor for IProjectNature conformance
+ */
+ public CVSTeamProvider() {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.resources.IProjectNature#deconfigure()
+ */
+ public void deconfigure() {
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.RepositoryProvider#deconfigured()
+ */
+ public void deconfigured() {
+ // when a nature is removed from the project, notify the synchronizer that
+ // we no longer need the sync info cached. This does not affect the actual CVS
+ // meta directories on disk, and will remain unless a client calls unmanage().
+ try {
+ EclipseSynchronizer.getInstance().deconfigure(getProject(), null);
+ internalSetWatchEditEnabled(null);
+ internalSetFetchAbsentDirectories(null);
+ } catch(CVSException e) {
+ // Log the exception and let the disconnect continue
+ CVSProviderPlugin.log(e);
+ }
+ ResourceStateChangeListeners.getListener().projectDeconfigured(getProject());
+ }
+ /**
+ * @see IProjectNature#getProject()
+ */
+ public IProject getProject() {
+ return project;
+ }
+
+ /**
+ * @see IProjectNature#setProject(IProject)
+ */
+ public void setProject(IProject project) {
+ this.project = project;
+ try {
+ this.workspaceRoot = new CVSWorkspaceRoot(project);
+ // Ensure that the project has CVS info
+ if (workspaceRoot.getLocalRoot().getFolderSyncInfo() == null) {
+ CVSProviderPlugin.log(new CVSException(new CVSStatus(CVSStatus.ERROR, Policy.bind("CVSTeamProvider.noFolderInfo", project.getName())))); //$NON-NLS-1$
+ }
+ } catch (CVSException e) {
+ // Ignore exceptions here. They will be surfaced elsewhere
+ }
+ }
+
+ /**
+ * Diff the resources with the repository and write the output to the provided
+ * PrintStream in a form that is usable as a patch. The patch is rooted at the
+ * project.
+ */
+ public void diff(IResource resource, LocalOption[] options, PrintStream stream,
+ IProgressMonitor progress) throws TeamException {
+
+ boolean includeNewFiles = false;
+ boolean doNotRecurse = false;
+ int format = STANDARD_FORMAT;
+
+ // Determine the command root and arguments arguments list
+ ICVSResource cvsResource = CVSWorkspaceRoot.getCVSResourceFor(resource);
+ ICVSFolder commandRoot;
+ String[] arguments;
+ if (cvsResource.isFolder()) {
+ commandRoot = (ICVSFolder)cvsResource;
+ arguments = new String[] {Session.CURRENT_LOCAL_FOLDER};
+ } else {
+ commandRoot = cvsResource.getParent();
+ arguments = new String[] {cvsResource.getName()};
+ }
+
+ Session s = new Session(workspaceRoot.getRemoteLocation(), commandRoot);
+ progress.beginTask(null, 100);
+ try {
+ s.open(Policy.subMonitorFor(progress, 20), false /* read-only */);
+ Command.DIFF.execute(s,
+ Command.NO_GLOBAL_OPTIONS,
+ options,
+ arguments,
+ new DiffListener(stream),
+ Policy.subMonitorFor(progress, 80));
+ } finally {
+ s.close();
+ progress.done();
+ }
+
+ // Append our diff output to the server diff output.
+ // Our diff output includes new files and new files in new directories.
+
+ for (int i = 0; i < options.length; i++) {
+ LocalOption option = options[i];
+ if (option.equals(Diff.INCLUDE_NEWFILES)) {
+ includeNewFiles = true;
+ } else if (option.equals(Diff.DO_NOT_RECURSE)) {
+ doNotRecurse = true;
+ } else if (option.equals(Diff.UNIFIED_FORMAT)) {
+ format = UNIFIED_FORMAT;
+ } else if (option.equals(Diff.CONTEXT_FORMAT)) {
+ format = CONTEXT_FORMAT;
+ }
+ }
+
+ if (includeNewFiles) {
+ newFileDiff(commandRoot, stream, doNotRecurse, format);
+ }
+ }
+
+ /**
+ * This diff adds new files and directories to the stream.
+ * @param resource
+ * @param stream
+ * @param doNotRecurse
+ * @param format
+ * @throws CVSException
+ */
+ private void newFileDiff(final ICVSFolder resource, final PrintStream stream, final boolean doNotRecurse, final int format) throws CVSException {
+ resource.accept(new ICVSResourceVisitor() {
+ public void visitFile(ICVSFile file) throws CVSException {
+ if (!(file.isIgnored() || file.isManaged())) {
+ addFileToDiff(resource, file, stream, format);
+ }
+ }
+ public void visitFolder(ICVSFolder folder) throws CVSException {
+ // Even if we are not supposed to recurse we still need to go into
+ // the root directory.
+ if (!folder.exists() || folder.isIgnored() || (doNotRecurse && !folder.equals(resource))) {
+ return;
+ } else {
+ folder.acceptChildren(this);
+ }
+ }
+ });
+ }
+
+ private void addFileToDiff(ICVSFolder cmdRoot, ICVSFile file, PrintStream stream, int format) throws CVSException {
+
+ String nullFilePrefix = ""; //$NON-NLS-1$
+ String newFilePrefix = ""; //$NON-NLS-1$
+ String positionInfo = ""; //$NON-NLS-1$
+ String linePrefix = ""; //$NON-NLS-1$
+
+ String pathString = file.getRelativePath(cmdRoot);
+
+ BufferedReader fileReader = new BufferedReader(new InputStreamReader(file.getContents()));
+ int lines = 0;
+ try {
+ while (fileReader.readLine() != null) {
+ lines++;
+ }
+ fileReader.close();
+
+ switch (format) {
+ case UNIFIED_FORMAT :
+ nullFilePrefix = "--- "; //$NON-NLS-1$
+ newFilePrefix = "+++ "; //$NON-NLS-1$
+ positionInfo = "@@ -0,0 +1," + lines + " @@" ; //$NON-NLS-1$ //$NON-NLS-2$
+ linePrefix = "+"; //$NON-NLS-1$
+ break;
+
+ case CONTEXT_FORMAT :
+ nullFilePrefix = "*** "; //$NON-NLS-1$
+ newFilePrefix = "--- "; //$NON-NLS-1$
+ positionInfo = "--- 1," + lines + " ----"; //$NON-NLS-1$ //$NON-NLS-2$
+ linePrefix = "+ "; //$NON-NLS-1$
+ break;
+
+ default :
+ positionInfo = "0a1," + lines; //$NON-NLS-1$
+ linePrefix = "> "; //$NON-NLS-1$
+ break;
+ }
+
+ fileReader = new BufferedReader(new InputStreamReader(file.getContents()));
+
+ stream.println("Index: " + pathString); //$NON-NLS-1$
+ stream.println("==================================================================="); //$NON-NLS-1$
+ stream.println("RCS file: " + pathString); //$NON-NLS-1$
+ stream.println("diff -N " + pathString); //$NON-NLS-1$
+
+ if (lines > 0) {
+
+ if (format != STANDARD_FORMAT) {
+ stream.println(nullFilePrefix + "/dev/null 1 Jan 1970 00:00:00 -0000"); //$NON-NLS-1$
+ // Technically this date should be the local file date but nobody really cares.
+ stream.println(newFilePrefix + pathString + " 1 Jan 1970 00:00:00 -0000"); //$NON-NLS-1$
+ }
+
+ if (format == CONTEXT_FORMAT) {
+ stream.println("***************"); //$NON-NLS-1$
+ stream.println("*** 0 ****"); //$NON-NLS-1$
+ }
+
+ stream.println(positionInfo);
+
+ for (int i = 0; i < lines; i++) {
+ stream.print(linePrefix);
+ stream.println(fileReader.readLine());
+ }
+ }
+ } catch (IOException e) {
+ throw CVSException.wrapException(file.getIResource(), Policy.bind("CVSTeamProvider.errorAddingFileToDiff", pathString), e); //$NON-NLS-1$
+ } finally {
+ try {
+ fileReader.close();
+ } catch (IOException e1) {
+ }
+ }
+ }
+
+ /**
+ * Return the remote location to which the receiver's project is mapped.
+ */
+ public ICVSRepositoryLocation getRemoteLocation() throws CVSException {
+ try {
+ return workspaceRoot.getRemoteLocation();
+ } catch (CVSException e) {
+ // If we can't get the remote location, we should disconnect since nothing can be done with the provider
+ try {
+ RepositoryProvider.unmap(project);
+ } catch (TeamException ex) {
+ CVSProviderPlugin.log(ex);
+ }
+ // We need to trigger a decorator refresh
+ throw e;
+ }
+ }
+
+ /*
+ * @see ITeamProvider#isDirty(IResource)
+ */
+ public boolean isDirty(IResource resource) {
+ Assert.isTrue(false);
+ return false;
+ }
+
+ public CVSWorkspaceRoot getCVSWorkspaceRoot() {
+ return workspaceRoot;
+ }
+
+ /*
+ * Generate an exception if the resource is not a child of the project
+ */
+ private void checkIsChild(IResource resource) throws CVSException {
+ if (!isChildResource(resource))
+ throw new CVSException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE,
+ Policy.bind("CVSTeamProvider.invalidResource", //$NON-NLS-1$
+ new Object[] {resource.getFullPath().toString(), project.getName()}),
+ null));
+ }
+
+ /*
+ * Get the arguments to be passed to a commit or update
+ */
+ private String[] getValidArguments(IResource[] resources, LocalOption[] options) throws CVSException {
+ List arguments = new ArrayList(resources.length);
+ for (int i=0;i<resources.length;i++) {
+ checkIsChild(resources[i]);
+ IPath cvsPath = resources[i].getFullPath().removeFirstSegments(1);
+ if (cvsPath.segmentCount() == 0) {
+ arguments.add(Session.CURRENT_LOCAL_FOLDER);
+ } else {
+ arguments.add(cvsPath.toString());
+ }
+ }
+ return (String[])arguments.toArray(new String[arguments.size()]);
+ }
+
+ private ICVSResource[] getCVSArguments(IResource[] resources) {
+ ICVSResource[] cvsResources = new ICVSResource[resources.length];
+ for (int i = 0; i < cvsResources.length; i++) {
+ cvsResources[i] = CVSWorkspaceRoot.getCVSResourceFor(resources[i]);
+ }
+ return cvsResources;
+ }
+
+ /*
+ * This method expects to be passed an InfiniteSubProgressMonitor
+ */
+ public void setRemoteRoot(ICVSRepositoryLocation location, IProgressMonitor monitor) throws TeamException {
+
+ // Check if there is a differnece between the new and old roots
+ final String root = location.getLocation();
+ if (root.equals(workspaceRoot.getRemoteLocation()))
+ return;
+
+ try {
+ workspaceRoot.getLocalRoot().run(new ICVSRunnable() {
+ public void run(IProgressMonitor progress) throws CVSException {
+ try {
+ // 256 ticks gives us a maximum of 1024 which seems reasonable for folders is a project
+ progress.beginTask(null, 100);
+ final IProgressMonitor monitor = Policy.infiniteSubMonitorFor(progress, 100);
+ monitor.beginTask(Policy.bind("CVSTeamProvider.folderInfo", project.getName()), 256); //$NON-NLS-1$
+
+ // Visit all the children folders in order to set the root in the folder sync info
+ workspaceRoot.getLocalRoot().accept(new ICVSResourceVisitor() {
+ public void visitFile(ICVSFile file) throws CVSException {}
+ public void visitFolder(ICVSFolder folder) throws CVSException {
+ monitor.worked(1);
+ FolderSyncInfo info = folder.getFolderSyncInfo();
+ if (info != null) {
+ monitor.subTask(Policy.bind("CVSTeamProvider.updatingFolder", info.getRepository())); //$NON-NLS-1$
+ folder.setFolderSyncInfo(new FolderSyncInfo(info.getRepository(), root, info.getTag(), info.getIsStatic()));
+ folder.acceptChildren(this);
+ }
+ }
+ });
+ } finally {
+ progress.done();
+ }
+ }
+ }, monitor);
+ } finally {
+ monitor.done();
+ }
+ }
+
+ /*
+ * Helper to indicate if the resource is a child of the receiver's project
+ */
+ private boolean isChildResource(IResource resource) {
+ return resource.getProject().getName().equals(project.getName());
+ }
+
+ public void configureProject() throws CoreException {
+ ResourceStateChangeListeners.getListener().projectConfigured(getProject());
+ }
+ /**
+ * Sets the keyword substitution mode for the specified resources.
+ * <p>
+ * Applies the following rules in order:<br>
+ * <ul>
+ * <li>If a file is not managed, skips it.</li>
+ * <li>If a file is not changing modes, skips it.</li>
+ * <li>If a file is being changed from binary to text, corrects line delimiters
+ * then commits it, then admins it.</li>
+ * <li>If a file is added, changes the resource sync information locally.</li>
+ * <li>Otherwise commits the file (with FORCE to create a new revision), then admins it.</li>
+ * </ul>
+ * All files that are admin'd are committed with FORCE to prevent other developers from
+ * casually trying to commit pending changes to the repository without first checking out
+ * a new copy. This is not a perfect solution, as they could just as easily do an UPDATE
+ * and not obtain the new keyword sync info.
+ * </p>
+ *
+ * @param changeSet a map from IFile to KSubstOption
+ * @param monitor the progress monitor
+ * @return a status code indicating success or failure of the operation
+ *
+ * @throws TeamException
+ */
+ public IStatus setKeywordSubstitution(final Map /* from IFile to KSubstOption */ changeSet,
+ final String comment,
+ IProgressMonitor monitor) throws TeamException {
+ final IStatus[] result = new IStatus[] { ICommandOutputListener.OK };
+ workspaceRoot.getLocalRoot().run(new ICVSRunnable() {
+ public void run(final IProgressMonitor monitor) throws CVSException {
+ final Map /* from KSubstOption to List of String */ filesToAdmin = new HashMap();
+ final List /* of ICVSResource */ filesToCommit = new ArrayList();
+ final Collection /* of ICVSFile */ filesToCommitAsText = new HashSet(); // need fast lookup
+ final boolean useCRLF = IS_CRLF_PLATFORM && (CVSProviderPlugin.getPlugin().isUsePlatformLineend());
+
+ /*** determine the resources to be committed and/or admin'd ***/
+ for (Iterator it = changeSet.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ IFile file = (IFile) entry.getKey();
+ KSubstOption toKSubst = (KSubstOption) entry.getValue();
+
+ // only set keyword substitution if resource is a managed file
+ checkIsChild(file);
+ ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor(file);
+ if (! mFile.isManaged()) continue;
+
+ // only set keyword substitution if new differs from actual
+ byte[] syncBytes = mFile.getSyncBytes();
+ KSubstOption fromKSubst = ResourceSyncInfo.getKeywordMode(syncBytes);
+ if (toKSubst.equals(fromKSubst)) continue;
+
+ // change resource sync info immediately for an outgoing addition
+ if (ResourceSyncInfo.isAddition(syncBytes)) {
+ mFile.setSyncBytes(ResourceSyncInfo.setKeywordMode(syncBytes, toKSubst), ICVSFile.UNKNOWN);
+ continue;
+ }
+
+ // nothing do to for deletions
+ if (ResourceSyncInfo.isDeletion(syncBytes)) continue;
+
+ // file exists remotely so we'll have to commit it
+ if (fromKSubst.isBinary() && ! toKSubst.isBinary()) {
+ // converting from binary to text
+ cleanLineDelimiters(file, useCRLF, new NullProgressMonitor()); // XXX need better progress monitoring
+ // remember to commit the cleaned resource as text before admin
+ filesToCommitAsText.add(mFile);
+ }
+ // force a commit to bump the revision number
+ makeDirty(file);
+ filesToCommit.add(mFile);
+ // remember to admin the resource
+ List list = (List) filesToAdmin.get(toKSubst);
+ if (list == null) {
+ list = new ArrayList();
+ filesToAdmin.put(toKSubst, list);
+ }
+ list.add(mFile);
+ }
+
+ /*** commit then admin the resources ***/
+ // compute the total work to be performed
+ int totalWork = filesToCommit.size() + 1;
+ for (Iterator it = filesToAdmin.values().iterator(); it.hasNext();) {
+ List list = (List) it.next();
+ totalWork += list.size();
+ totalWork += 1; // Add 1 for each connection that needs to be made
+ }
+ if (totalWork != 0) {
+ monitor.beginTask(Policy.bind("CVSTeamProvider.settingKSubst"), totalWork); //$NON-NLS-1$
+ try {
+ // commit files that changed from binary to text
+ // NOTE: The files are committed as text with conversions even if the
+ // resource sync info still says "binary".
+ if (filesToCommit.size() != 0) {
+ Session session = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot(), true /* output to console */);
+ session.open(Policy.subMonitorFor(monitor, 1), true /* open for modification */);
+ try {
+ String keywordChangeComment = comment;
+ if (keywordChangeComment == null || keywordChangeComment.length() == 0)
+ keywordChangeComment = Policy.bind("CVSTeamProvider.changingKeywordComment"); //$NON-NLS-1$
+ result[0] = Command.COMMIT.execute(
+ session,
+ Command.NO_GLOBAL_OPTIONS,
+ new LocalOption[] { Commit.DO_NOT_RECURSE, Commit.FORCE,
+ Commit.makeArgumentOption(Command.MESSAGE_OPTION, keywordChangeComment) },
+ (ICVSResource[]) filesToCommit.toArray(new ICVSResource[filesToCommit.size()]),
+ filesToCommitAsText,
+ null,
+ Policy.subMonitorFor(monitor, filesToCommit.size()));
+ } finally {
+ session.close();
+ }
+
+ // if errors were encountered, abort
+ if (! result[0].isOK()) return;
+ }
+
+ // admin files that changed keyword substitution mode
+ // NOTE: As confirmation of the completion of a command, the server replies
+ // with the RCS command output if a change took place. Rather than
+ // assume that the command succeeded, we listen for these lines
+ // and update the local ResourceSyncInfo for the particular files that
+ // were actually changed remotely.
+ for (Iterator it = filesToAdmin.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ final KSubstOption toKSubst = (KSubstOption) entry.getKey();
+ final List list = (List) entry.getValue();
+ // do it
+ Session session = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot(), true /* output to console */);
+ session.open(Policy.subMonitorFor(monitor, 1), true /* open for modification */);
+ try {
+ result[0] = Command.ADMIN.execute(
+ session,
+ Command.NO_GLOBAL_OPTIONS,
+ new LocalOption[] { toKSubst },
+ (ICVSResource[]) list.toArray(new ICVSResource[list.size()]),
+ new AdminKSubstListener(toKSubst),
+ Policy.subMonitorFor(monitor, list.size()));
+ } finally {
+ session.close();
+ }
+ // if errors were encountered, abort
+ if (! result[0].isOK()) return;
+ }
+ } finally {
+ monitor.done();
+ }
+ }
+ }
+ }, Policy.monitorFor(monitor));
+ return result[0];
+ }
+
+ /**
+ * This method translates the contents of a file from binary into text (ASCII).
+ * Fixes the line delimiters in the local file to reflect the platform's
+ * native encoding. Performs CR/LF -> LF or LF -> CR/LF conversion
+ * depending on the platform but does not affect delimiters that are
+ * already correctly encoded.
+ */
+ public static void cleanLineDelimiters(IFile file, boolean useCRLF, IProgressMonitor progress)
+ throws CVSException {
+ try {
+ // convert delimiters in memory
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ InputStream is = new BufferedInputStream(file.getContents());
+ try {
+ // Always convert CR/LF into LFs
+ is = new CRLFtoLFInputStream(is);
+ if (useCRLF) {
+ // For CR/LF platforms, translate LFs to CR/LFs
+ is = new LFtoCRLFInputStream(is);
+ }
+ for (int b; (b = is.read()) != -1;) bos.write(b);
+ bos.close();
+ } finally {
+ is.close();
+ }
+ // write file back to disk with corrected delimiters if changes were made
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ file.setContents(bis, false /*force*/, false /*keepHistory*/, progress);
+ } catch (CoreException e) {
+ throw CVSException.wrapException(file, Policy.bind("CVSTeamProvider.cleanLineDelimitersException"), e); //$NON-NLS-1$
+ } catch (IOException e) {
+ throw CVSException.wrapException(file, Policy.bind("CVSTeamProvider.cleanLineDelimitersException"), e); //$NON-NLS-1$
+ }
+ }
+
+ /*
+ * Marks a file as dirty.
+ */
+ private static void makeDirty(IFile file) throws CVSException {
+ ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor(file);
+ ResourceSyncInfo origInfo = mFile.getSyncInfo();
+ MutableResourceSyncInfo info = origInfo.cloneMutable();
+ info.setTimeStamp(null);/*set the sync timestamp to null to trigger dirtyness*/
+ mFile.setSyncInfo(info, ICVSFile.UNKNOWN);
+ }
+
+ /*
+ * @see RepositoryProvider#getID()
+ */
+ public String getID() {
+ return CVSProviderPlugin.getTypeId();
+ }
+
+ /*
+ * @see RepositoryProvider#getMoveDeleteHook()
+ */
+ public IMoveDeleteHook getMoveDeleteHook() {
+ return moveDeleteHook;
+ }
+
+ /*
+ * Return the currently registered Move/Delete Hook
+ */
+ public static MoveDeleteHook getRegisteredMoveDeleteHook() {
+ return moveDeleteHook;
+ }
+
+ /**
+ * @see org.eclipse.team.core.RepositoryProvider#getFileModificationValidator()
+ */
+ public IFileModificationValidator getFileModificationValidator() {
+ if (CVSTeamProvider.fileModificationValidator == null) {
+ CVSTeamProvider.fileModificationValidator = new CVSCoreFileModificationValidator();
+ }
+ return CVSTeamProvider.fileModificationValidator;
+ }
+
+ /**
+ * Checkout (cvs edit) the provided resources so they can be modified locally and committed.
+ * This will make any read-only resources in the list writable and will notify the server
+ * that the file is being edited. This notification may be done immediately or at some
+ * later point depending on whether contact with the server is possble at the time of
+ * invocation or the value of the notify server parameter.
+ *
+ * The recurse parameter is equivalent to the cvs local options -l (<code>true</code>) and
+ * -R (<code>false</code>). The notifyServer parameter can be used to defer server contact
+ * until the next command. This may be approrpiate if no shell or progress monitor is available
+ * to the caller. The notification bit field indicates what temporary watches are to be used while
+ * the file is being edited. The possible values that can be ORed together are ICVSFile.EDIT,
+ * ICVSFile.UNEDIT and ICVSFile.COMMIT. There pre-ORed convenience values ICVSFile.NO_NOTIFICATION
+ * and ICVSFile.NOTIFY_ON_ALL are also available.
+ *
+ * @param resources the resources to be edited
+ * @param recurse indicates whether to recurse (-R) or not (-l)
+ * @param notifyServer indicates whether to notify the server now, if possible,
+ * or defer until the next command.
+ * @param notification the temporary watches.
+ * @param progress progress monitor to provide progress indication/cancellation or <code>null</code>
+ * @exception CVSException if this method fails.
+ * @since 2.1
+ *
+ * @see CVSTeamProvider#unedit
+ */
+ public void edit(IResource[] resources, boolean recurse, boolean notifyServer, final int notification, IProgressMonitor progress) throws CVSException {
+ notifyEditUnedit(resources, recurse, notifyServer, new ICVSResourceVisitor() {
+ public void visitFile(ICVSFile file) throws CVSException {
+ if (file.isReadOnly())
+ file.edit(notification, Policy.monitorFor(null));
+ }
+ public void visitFolder(ICVSFolder folder) throws CVSException {
+ // nothing needs to be done here as the recurse will handle the traversal
+ }
+ }, null /* no scheduling rule */, progress);
+ }
+
+ /**
+ * Unedit the given resources. Any writtable resources will be reverted to their base contents
+ * and made read-only and the server will be notified that the file is no longer being edited.
+ * This notification may be done immediately or at some
+ * later point depending on whether contact with the server is possble at the time of
+ * invocation or the value of the notify server parameter.
+ *
+ * The recurse parameter is equivalent to the cvs local options -l (<code>true</code>) and
+ * -R (<code>false</code>). The notifyServer parameter can be used to defer server contact
+ * until the next command. This may be approrpiate if no shell or progress monitor is available
+ * to the caller.
+ *
+ * @param resources the resources to be unedited
+ * @param recurse indicates whether to recurse (-R) or not (-l)
+ * @param notifyServer indicates whether to notify the server now, if possible,
+ * or defer until the next command.
+ * @param progress progress monitor to provide progress indication/cancellation or <code>null</code>
+ * @exception CVSException if this method fails.
+ * @since 2.1
+ *
+ * @see CVSTeamProvider#edit
+ */
+ public void unedit(IResource[] resources, boolean recurse, boolean notifyServer, IProgressMonitor progress) throws CVSException {
+ notifyEditUnedit(resources, recurse, notifyServer, new ICVSResourceVisitor() {
+ public void visitFile(ICVSFile file) throws CVSException {
+ if (!file.isReadOnly())
+ file.unedit(Policy.monitorFor(null));
+ }
+ public void visitFolder(ICVSFolder folder) throws CVSException {
+ // nothing needs to be done here as the recurse will handle the traversal
+ }
+ }, getProject() /* project scheduling rule */, progress);
+ }
+
+ /*
+ * This method captures the common behavior between the edit and unedit methods.
+ */
+ private void notifyEditUnedit(final IResource[] resources, final boolean recurse, final boolean notifyServer, final ICVSResourceVisitor editUneditVisitor, ISchedulingRule rule, IProgressMonitor monitor) throws CVSException {
+ final CVSException[] exception = new CVSException[] { null };
+ IWorkspaceRunnable workspaceRunnable = new IWorkspaceRunnable() {
+ public void run(IProgressMonitor monitor) throws CoreException {
+ final ICVSResource[] cvsResources = getCVSArguments(resources);
+
+ // mark the files locally as being checked out
+ try {
+ for (int i = 0; i < cvsResources.length; i++) {
+ cvsResources[i].accept(editUneditVisitor, recurse);
+ }
+ } catch (CVSException e) {
+ exception[0] = e;
+ return;
+ }
+
+ // send the noop command to the server in order to deliver the notifications
+ if (notifyServer) {
+ monitor.beginTask(null, 100);
+ Session session = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot(), true);
+ try {
+ try {
+ session.open(Policy.subMonitorFor(monitor, 10), true /* open for modification */);
+ } catch (CVSException e1) {
+ // If the connection cannot be opened, just exit normally.
+ // The notifications will be sent when a connection can be made
+ return;
+ }
+ Command.NOOP.execute(
+ session,
+ Command.NO_GLOBAL_OPTIONS,
+ Command.NO_LOCAL_OPTIONS,
+ cvsResources,
+ null,
+ Policy.subMonitorFor(monitor, 90));
+ } catch (CVSException e) {
+ exception[0] = e;
+ } finally {
+ session.close();
+ monitor.done();
+ }
+ }
+ }
+ };
+ try {
+ ResourcesPlugin.getWorkspace().run(workspaceRunnable, rule, 0, Policy.monitorFor(monitor));
+ } catch (CoreException e) {
+ if (exception[0] == null) {
+ throw CVSException.wrapException(e);
+ } else {
+ CVSProviderPlugin.log(CVSException.wrapException(e));
+ }
+ }
+ if (exception[0] != null) {
+ throw exception[0];
+ }
+ }
+
+ /**
+ * Gets the etchAbsentDirectories.
+ * @return Returns a boolean
+ */
+ public boolean getFetchAbsentDirectories() throws CVSException {
+ try {
+ String property = getProject().getPersistentProperty(FETCH_ABSENT_DIRECTORIES_PROP_KEY);
+ if (property == null) return CVSProviderPlugin.getPlugin().getFetchAbsentDirectories();
+ return Boolean.valueOf(property).booleanValue();
+ } catch (CoreException e) {
+ throw new CVSException(new CVSStatus(IStatus.ERROR, Policy.bind("CVSTeamProvider.errorGettingFetchProperty", project.getName()), e)); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Sets the fetchAbsentDirectories.
+ * @param etchAbsentDirectories The etchAbsentDirectories to set
+ */
+ public void setFetchAbsentDirectories(boolean fetchAbsentDirectories) throws CVSException {
+ internalSetFetchAbsentDirectories(fetchAbsentDirectories ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ }
+
+ public void internalSetFetchAbsentDirectories(String fetchAbsentDirectories) throws CVSException {
+ try {
+ getProject().setPersistentProperty(FETCH_ABSENT_DIRECTORIES_PROP_KEY, fetchAbsentDirectories);
+ } catch (CoreException e) {
+ throw new CVSException(new CVSStatus(IStatus.ERROR, Policy.bind("CVSTeamProvider.errorSettingFetchProperty", project.getName()), e)); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * @see org.eclipse.team.core.RepositoryProvider#canHandleLinkedResources()
+ */
+ public boolean canHandleLinkedResources() {
+ return true;
+ }
+
+ /**
+ * @see org.eclipse.team.core.RepositoryProvider#validateCreateLink(org.eclipse.core.resources.IResource, int, org.eclipse.core.runtime.IPath)
+ */
+ public IStatus validateCreateLink(IResource resource, int updateFlags, IPath location) {
+ ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor(resource.getParent().getFolder(new Path(resource.getName())));
+ try {
+ if (cvsFolder.isCVSFolder()) {
+ // There is a remote folder that overlaps with the link so disallow
+ return new CVSStatus(IStatus.ERROR, Policy.bind("CVSTeamProvider.overlappingRemoteFolder", resource.getFullPath().toString())); //$NON-NLS-1$
+ } else {
+ ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(resource.getParent().getFile(new Path(resource.getName())));
+ if (cvsFile.isManaged()) {
+ // there is an outgoing file deletion that overlaps the link so disallow
+ return new CVSStatus(IStatus.ERROR, Policy.bind("CVSTeamProvider.overlappingFileDeletion", resource.getFullPath().toString())); //$NON-NLS-1$
+ }
+ }
+ } catch (CVSException e) {
+ CVSProviderPlugin.log(e);
+ return e.getStatus();
+ }
+
+ return super.validateCreateLink(resource, updateFlags, location);
+ }
+
+ /**
+ * Get the editors of the resources by calling the <code>cvs editors</code> command.
+ *
+ * @author <a href="mailto:gregor.kohlwes@csc.com,kohlwes@gmx.net">Gregor Kohlwes</a>
+ * @param resources
+ * @param progress
+ * @return IEditorsInfo[]
+ * @throws CVSException
+ */
+ public EditorsInfo[] editors(
+ IResource[] resources,
+ IProgressMonitor progress)
+ throws CVSException {
+
+ // Build the local options
+ LocalOption[] commandOptions = new LocalOption[] {
+ };
+ progress.worked(10);
+ // Build the arguments list
+ String[] arguments = getValidArguments(resources, commandOptions);
+
+ // Build the listener for the command
+ EditorsListener listener = new EditorsListener();
+
+ // Check if canceled
+ if (progress.isCanceled()) {
+ return new EditorsInfo[0];
+ }
+ // Build the session
+ Session session =
+ new Session(
+ workspaceRoot.getRemoteLocation(),
+ workspaceRoot.getLocalRoot());
+
+ // Check if canceled
+ if (progress.isCanceled()) {
+ return new EditorsInfo[0];
+ }
+ progress.beginTask(null, 100);
+ try {
+ // Opening the session takes 20% of the time
+ session.open(Policy.subMonitorFor(progress, 20), false /* read-only */);
+
+ if (!progress.isCanceled()) {
+ // Execute the editors command
+ Command.EDITORS.execute(
+ session,
+ Command.NO_GLOBAL_OPTIONS,
+ commandOptions,
+ arguments,
+ listener,
+ Policy.subMonitorFor(progress, 80));
+ }
+ } finally {
+ session.close();
+ progress.done();
+ }
+ // Return the infos about the editors
+ return listener.getEditorsInfos();
+ }
+
+ /**
+ * Return the commit comment template that was provided by the server.
+ *
+ * @return String
+ * @throws CVSException
+ */
+ public String getCommitTemplate() throws CVSException {
+ ICVSFolder localFolder = getCVSWorkspaceRoot().getLocalRoot();
+ ICVSFile templateFile = CVSWorkspaceRoot.getCVSFileFor(
+ SyncFileWriter.getTemplateFile(
+ (IContainer)localFolder.getIResource()));
+ if (!templateFile.exists()) return null;
+ InputStream in = new BufferedInputStream(templateFile.getContents());
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ int b;
+ do {
+ b = in.read();
+ if (b != -1)
+ out.write((byte)b);
+ } while (b != -1);
+ out.close();
+ return new String(out.toString());
+ } catch (IOException e) {
+ throw CVSException.wrapException(e);
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // Since we already have the contents, just log this exception
+ CVSProviderPlugin.log(CVSException.wrapException(e));
+ }
+ }
+ }
+
+ /**
+ * Return true if the project is configured to use watch/edit. A project will use
+ * watch/edit if it was checked out when the global preference to use watch/edit is
+ * turned on.
+ * @return boolean
+ */
+ public boolean isWatchEditEnabled() throws CVSException {
+ IProject project = getProject();
+ try {
+ String property = (String)project.getSessionProperty(WATCH_EDIT_PROP_KEY);
+ if (property == null) {
+ property = project.getPersistentProperty(WATCH_EDIT_PROP_KEY);
+ if (property == null) {
+ // The persistant property for the project was never set (i.e. old project)
+ // Use the global preference to determine if the project is using watch/edit
+ return CVSProviderPlugin.getPlugin().isWatchEditEnabled();
+ } else {
+ project.setSessionProperty(WATCH_EDIT_PROP_KEY, property);
+ }
+ }
+ return Boolean.valueOf(property).booleanValue();
+ } catch (CoreException e) {
+ if (project.isAccessible()) {
+ // We only care if the project still exists
+ throw new CVSException(new CVSStatus(IStatus.ERROR, Policy.bind("CVSTeamProvider.errorGettingWatchEdit", project.getName()), e)); //$NON-NLS-1$
+ }
+ }
+ return false;
+ }
+
+ public void setWatchEditEnabled(boolean enabled) throws CVSException {
+ internalSetWatchEditEnabled(enabled ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
+ }
+
+ private void internalSetWatchEditEnabled(String enabled) throws CVSException {
+ try {
+ IProject project = getProject();
+ project.setPersistentProperty(WATCH_EDIT_PROP_KEY, enabled);
+ project.setSessionProperty(WATCH_EDIT_PROP_KEY, enabled);
+ } catch (CoreException e) {
+ throw new CVSException(new CVSStatus(IStatus.ERROR, Policy.bind("CVSTeamProvider.errorSettingWatchEdit", project.getName()), e)); //$NON-NLS-1$
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.core.RepositoryProvider#getRuleFactory()
+ */
+ public IResourceRuleFactory getRuleFactory() {
+ return RESOURCE_RULE_FACTORY;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Add.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Add.java
new file mode 100644
index 000000000..157584c3c
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/Add.java
@@ -0,0 +1,100 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.core.client;
+
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSStatus;
+import org.eclipse.team.internal.ccvs.core.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener;
+import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+
+public class Add extends Command {
+ /*** Local options: specific to add ***/
+
+ protected Add() { }
+ protected String getRequestId() {
+ return "add"; //$NON-NLS-1$
+ }
+
+ protected ICVSResource[] sendLocalResourceState(Session session, GlobalOption[] globalOptions,
+ LocalOption[] localOptions, ICVSResource[] resources, IProgressMonitor monitor)
+ throws CVSException {
+
+ // Check that all the arguments can give you an
+ // repo that you will need while traversing the
+ // file-structure
+ for (int i = 0; i < resources.length; i++) {
+ Assert.isNotNull(resources[i].getRemoteLocation(session.getLocalRoot()));
+ }
+
+ // Get a vistor and use it on every resource we should
+ // work on
+ AddStructureVisitor visitor = new AddStructureVisitor(session);
+ visitor.visit(session, resources, monitor);
+ return resources;
+ }
+
+ /**
+ * If the add succeeded then folders have to be initialized with the
+ * sync info
+ */
+ protected IStatus commandFinished(Session session, GlobalOption[] globalOptions,
+ LocalOption[] localOptions, ICVSResource[] resources, IProgressMonitor monitor,
+ IStatus status) throws CVSException {
+
+ if (status.getCode() == CVSStatus.SERVER_ERROR) {
+ return status;
+ }
+
+ for (int i = 0; i < resources.length; i++) {
+ if (resources[i].isFolder()) {
+ ICVSFolder mFolder = (ICVSFolder) resources[i];
+ FolderSyncInfo info = mFolder.getParent().getFolderSyncInfo();
+ if (info == null) {
+ status = mergeStatus(status, new CVSStatus(CVSStatus.ERROR, Policy.bind("Add.invalidParent", mFolder.getRelativePath(session.getLocalRoot())))); //$NON-NLS-1$
+ } else {
+ String repository = info.getRepository() + "/" + mFolder.getName(); //$NON-NLS-1$
+ mFolder.setFolderSyncInfo(new FolderSyncInfo(repository, info.getRoot(), info.getTag(), info.getIsStatic()));
+ }
+ }
+ }
+ return status;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.core.client.Command#getDefaultCommandOutputListener()
+ */
+ protected ICommandOutputListener getDefaultCommandOutputListener() {
+ return new CommandOutputListener() {
+ public IStatus errorLine(String line,
+ ICVSRepositoryLocation location, ICVSFolder commandRoot,
+ IProgressMonitor monitor) {
+
+ String serverMessage = getServerMessage(line, location);
+ if (serverMessage != null) {
+ if (serverMessage.startsWith("use `cvs commit' to add")) //$NON-NLS-1$
+ return OK;
+ if (serverMessage.startsWith("scheduling file") && serverMessage.endsWith("for addition")) //$NON-NLS-1$ //$NON-NLS-2$
+ return OK;
+ }
+ return super.errorLine(line, location, commandRoot, monitor);
+ }
+ };
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/CommandOutputListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/CommandOutputListener.java
new file mode 100644
index 000000000..e510598d7
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/CommandOutputListener.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.core.client;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.internal.ccvs.core.CVSStatus;
+import org.eclipse.team.internal.ccvs.core.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
+import org.eclipse.team.internal.ccvs.core.client.listeners.ICommandOutputListener;
+import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
+
+public class CommandOutputListener implements ICommandOutputListener {
+
+ /*
+ * Failure string that is returned from the server when pserver is used and the root directory
+ * is not readable. The problem can be fixed by making the directory readable or by using -f in
+ * the pserver configuration file. We will ignore the error since it does not affect the command.
+ */
+ public static final String ROOT_CVSIGNORE_READ_FAILURE = "cvs server: cannot open /root/.cvsignore: Permission denied"; //$NON-NLS-1$
+
+ public IStatus messageLine(String line, ICVSRepositoryLocation location, ICVSFolder commandRoot, IProgressMonitor monitor) {
+ return OK;
+ }
+ public IStatus errorLine(String line, ICVSRepositoryLocation location, ICVSFolder commandRoot, IProgressMonitor monitor) {
+ String protocolError = getProtocolError(line, location);
+ if (protocolError != null) {
+ return new CVSStatus(CVSStatus.ERROR, CVSStatus.PROTOCOL_ERROR, commandRoot, protocolError);
+ }
+ if (line.equals(ROOT_CVSIGNORE_READ_FAILURE) || getServerMessage(ROOT_CVSIGNORE_READ_FAILURE, location).equals(getServerMessage(line, location))) {
+ // Don't report this as an error since it does not affect the command
+ return new CVSStatus(CVSStatus.WARNING, CVSStatus.ERROR_LINE, commandRoot, line);
+ }
+ return new CVSStatus(CVSStatus.ERROR, CVSStatus.ERROR_LINE, commandRoot, line);
+ }
+
+ /**
+ * Return the portion of the line that describes the error if the error line
+ * is a protocol error or null if the line is not a protocol error.
+ *
+ * @param line the error line received from the server
+ * @param location the repository location
+ * @return String the potocol error or null
+ */
+ protected String getProtocolError(String line, ICVSRepositoryLocation location) {
+ if (line.startsWith("Protocol error:")) { //$NON-NLS-1$
+ return line;
+ }
+ return null;
+ }
+
+ public String getServerMessage(String line, ICVSRepositoryLocation location) {
+ return ((CVSRepositoryLocation)location).getServerMessageWithoutPrefix(line, SERVER_PREFIX);
+ }
+
+ public String getServerAbortedMessage(String line, ICVSRepositoryLocation location) {
+ return ((CVSRepositoryLocation)location).getServerMessageWithoutPrefix(line, SERVER_ABORTED_PREFIX);
+ }
+
+ public String getServerRTagMessage(String line, ICVSRepositoryLocation location) {
+ return ((CVSRepositoryLocation)location).getServerMessageWithoutPrefix(line, RTAG_PREFIX);
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/AdminKSubstListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/AdminKSubstListener.java
new file mode 100644
index 000000000..c3dbf3348
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/AdminKSubstListener.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.core.client.listeners;
+
+
+import org.eclipse.core.runtime.*;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.client.CommandOutputListener;
+import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
+import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
+import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+/**
+ * Used with 'admin -ksubst' to capture lines of text that are issued
+ * as confirmation that the remote keyword substitution mode has been
+ * changed. When encountered, updates the local ResourceSyncInfo for
+ * the file in question to reflect
+ *
+ * e.g.
+ * RCS file: path/filename,v
+ * done
+ *
+ * We don't expect to see anything special on stderr if the command succeeds.
+ */
+public class AdminKSubstListener extends CommandOutputListener {
+ private KSubstOption ksubstMode;
+
+ public AdminKSubstListener(KSubstOption ksubstMode) {
+ this.ksubstMode = ksubstMode;
+ }
+
+ public IStatus messageLine(String line, ICVSRepositoryLocation location, ICVSFolder commandRoot,
+ IProgressMonitor monitor) {
+ if (line.startsWith("RCS file:")) { //$NON-NLS-1$
+ String rcsFile = line.substring(10).trim();
+ if (! rcsFile.endsWith(",v")) { //$NON-NLS-1$
+ return new CVSStatus(CVSStatus.ERROR,
+ Policy.bind("AdminKSubstListener.expectedRCSFile", rcsFile)); //$NON-NLS-1$
+ }
+ IPath rcsFilePath = new Path(null, Util.removeAtticSegment(rcsFile.substring(0, rcsFile.length() - 2)));
+ try {
+ ICVSFile file = findLocalFileFor(commandRoot, rcsFilePath);
+ //ResourceSyncInfo info = file.getSyncInfo();
+ byte[] syncBytes = file.getSyncBytes();
+ if (syncBytes != null) {
+ // only update sync info if we have it locally
+ file.setSyncBytes(ResourceSyncInfo.setKeywordMode(syncBytes, ksubstMode), ICVSFile.UNKNOWN);
+ }
+ } catch (CVSException e) {
+ return e.getStatus();
+ }
+ }
+ return OK;
+ }
+
+ private ICVSFile findLocalFileFor(ICVSFolder commandRoot, IPath rcsFilePath) throws CVSException {
+
+ // First, look for the local file by following the remote path
+ FolderSyncInfo info = commandRoot.getFolderSyncInfo();
+ String remoteRootLocation = info.getRemoteLocation();
+ if (remoteRootLocation == null) {
+ throw new CVSException(new CVSStatus(CVSStatus.ERROR,
+ Policy.bind("AdminKSubstListener.commandRootNotManaged"))); //$NON-NLS-1$
+ }
+ IPath remoteRootPath = new Path(null, remoteRootLocation);
+ if (remoteRootPath.isPrefixOf(rcsFilePath)) {
+ IPath relativeFilePath = rcsFilePath.removeFirstSegments(remoteRootPath.segmentCount());
+ ICVSFile file = commandRoot.getFile(relativeFilePath.toString());
+ if (file.isManaged() && isMatchingPath(file, rcsFilePath)) {
+ return file;
+ }
+ }
+
+ // We couldn't find the file that way which means we're working in a defined module.
+ // Scan all folders looking for a match
+ ICVSFolder parent = findFolder(commandRoot, rcsFilePath.removeLastSegments(1));
+ if (parent != null) {
+ ICVSFile file = parent.getFile(rcsFilePath.lastSegment());
+ if (file.isManaged()) {
+ return file;
+ }
+ }
+
+ // No file was found so return null;
+ throw new CVSException(new CVSStatus(CVSStatus.ERROR,
+ Policy.bind("AdminKSubstListener.expectedChildOfCommandRoot", //$NON-NLS-1$
+ rcsFilePath.toString(), remoteRootPath.toString())));
+ }
+
+ private ICVSFolder findFolder(ICVSFolder commandRoot, IPath path) throws CVSException {
+ final String remotePath = path.toString();
+ final ICVSFolder[] result = new ICVSFolder[] { null };
+ commandRoot.accept(new ICVSResourceVisitor() {
+ public void visitFile(ICVSFile file) throws CVSException {
+ // Nothing to do for files
+ }
+ public void visitFolder(ICVSFolder folder) throws CVSException {
+ FolderSyncInfo info = folder.getFolderSyncInfo();
+ if (info != null && info.getRemoteLocation().equals(remotePath)) {
+ // We found the folder we're looking for
+ result[0] = folder;
+ }
+ if (result[0] == null) {
+ folder.acceptChildren(this);
+ }
+ }
+ });
+ return result[0];
+ }
+
+ private boolean isMatchingPath(ICVSFile file, IPath rcsFilePath) throws CVSException {
+ FolderSyncInfo info = file.getParent().getFolderSyncInfo();
+ return info != null
+ && info.getRemoteLocation().equals(rcsFilePath.removeLastSegments(1).toString());
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/LogListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/LogListener.java
new file mode 100644
index 000000000..ab505da61
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/LogListener.java
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.core.client.listeners;
+
+import java.util.*;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.client.CommandOutputListener;
+import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+/**
+ * Log listener that parses the log entries returned from the
+ * server but delegates the handling of the entries to a subclass.
+ */
+public class LogListener extends CommandOutputListener {
+
+ // Server message prefix used for error detection
+ private static final String NOTHING_KNOWN_ABOUT = "nothing known about "; //$NON-NLS-1$
+
+ // States of log accumulation.
+ private final int DONE = 4;
+ private final int COMMENT = 3;
+ private final int REVISION = 2;
+ private final int SYMBOLIC_NAMES = 1;
+ private final int BEGIN = 0;
+
+ // Instance variables for accumulating Log information
+ private RemoteFile currentFile;
+ private int state = BEGIN;
+ private StringBuffer comment;
+ private String fileState;
+ private String revision;
+ private String author;
+ private String creationDate;
+ private List tagRevisions = new ArrayList(5);
+ private List tagNames = new ArrayList(5);
+
+ private final ILogEntryListener listener;
+
+ /**
+ * Create a log listener for receiving entries for one or more files.
+ */
+ public LogListener(ILogEntryListener listener) {
+ this.listener = listener;
+ }
+
+ public LogListener(RemoteFile file, ILogEntryListener listener) {
+ this(listener);
+ this.currentFile = file;
+ }
+
+ private String getRelativeFilePath(ICVSRepositoryLocation location, String fileName) {
+ if (fileName.endsWith(",v")) { //$NON-NLS-1$
+ fileName = fileName.substring(0, fileName.length() - 2);
+ }
+ fileName = Util.removeAtticSegment(fileName);
+ String rootDirectory = location.getRootDirectory();
+ if (fileName.startsWith(rootDirectory)) {
+ try {
+ fileName = Util.getRelativePath(rootDirectory, fileName);
+ } catch (CVSException e) {
+ CVSProviderPlugin.log(e);
+ return null;
+ }
+ }
+ return fileName;
+ }
+
+ public IStatus errorLine(String line, ICVSRepositoryLocation location, ICVSFolder commandRoot, IProgressMonitor monitor) {
+ String serverMessage = getServerMessage(line, location);
+ if (serverMessage != null) {
+ // look for the following condition
+ // E cvs server: nothing known about fileName
+ if (serverMessage.startsWith(NOTHING_KNOWN_ABOUT)) {
+ return new CVSStatus(IStatus.ERROR, CVSStatus.DOES_NOT_EXIST, commandRoot, line);
+ }
+ }
+ return OK;
+ }
+
+ public IStatus messageLine(String line, ICVSRepositoryLocation location, ICVSFolder commandRoot, IProgressMonitor monitor) {
+ // Fields we will find in the log for a file
+ // keys = String (tag name), values = String (tag revision number) */
+ switch (state) {
+ case BEGIN:
+ if (line.startsWith("RCS file: ")) { //$NON-NLS-1$
+ // We are starting to recieve the log for a file
+ String fileName = getRelativeFilePath(location, line.substring(10).trim());
+ if (fileName == null) {
+ currentFile = null;
+ handleInvalidFileName(location, fileName);
+ } else {
+ if (currentFile == null || !currentFile.getRepositoryRelativePath().equals(fileName)) {
+ // We are starting another file
+ beginFile(location, fileName);
+ }
+ }
+ } else if (line.startsWith("symbolic names:")) { //$NON-NLS-1$
+ state = SYMBOLIC_NAMES;
+ } else if (line.startsWith("revision ")) { //$NON-NLS-1$
+ revision = line.substring(9);
+ state = REVISION;
+ }
+ break;
+ case SYMBOLIC_NAMES:
+ if (line.startsWith("keyword substitution:")) { //$NON-NLS-1$
+ state = BEGIN;
+ } else {
+ int firstColon = line.indexOf(':');
+ String tagName = line.substring(1, firstColon);
+ String tagRevision = line.substring(firstColon + 2);
+ tagNames.add(tagName);
+ tagRevisions.add(tagRevision);
+ }
+ break;
+ case REVISION:
+ // date: 2000/06/19 04:56:21; author: somebody; state: Exp; lines: +114 -45
+ // get the creation date
+ int endOfDateIndex = line.indexOf(';', 6);
+ creationDate = line.substring(6, endOfDateIndex) + " GMT"; //$NON-NLS-1$
+
+ // get the author name
+ int endOfAuthorIndex = line.indexOf(';', endOfDateIndex + 1);
+ author = line.substring(endOfDateIndex + 11, endOfAuthorIndex);
+
+ // get the file state (because this revision might be "dead")
+ fileState = line.substring(endOfAuthorIndex + 10, line.indexOf(';', endOfAuthorIndex + 1));
+ comment = new StringBuffer();
+ state = COMMENT;
+ break;
+ case COMMENT:
+ // skip next line (info about branches) if it exists, if not then it is a comment line.
+ if (line.startsWith("branches:")) break; //$NON-NLS-1$
+ if (line.equals("=============================================================================") //$NON-NLS-1$
+ || line.equals("----------------------------")) { //$NON-NLS-1$
+ state = DONE;
+ break;
+ }
+ if (comment.length() != 0) comment.append('\n');
+ comment.append(line);
+ break;
+ }
+ if (state == DONE) {
+ // we are only interested in tag names for this revision, remove all others.
+ List thisRevisionTags = new ArrayList(3);
+ for (int i = 0; i < tagNames.size(); i++) {
+ String tagName = (String) tagNames.get(i);
+ String tagRevision = (String) tagRevisions.get(i);
+ // If this is a branch tag then only include this tag with the revision
+ // that is the root of this branch (e.g. 1.1 is root of branch 1.1.2).
+ boolean isBranch = isBranchTag(tagRevision);
+ if (isBranch) {
+ int lastDot = tagRevision.lastIndexOf('.');
+ if (lastDot == -1) {
+ CVSProviderPlugin.log(IStatus.ERROR,
+ Policy.bind("LogListener.invalidRevisionFormat", tagName, tagRevision), null); //$NON-NLS-1$
+ } else {
+ if (tagRevision.charAt(lastDot - 1) == '0' && tagRevision.charAt(lastDot - 2) == '.') {
+ lastDot = lastDot - 2;
+ }
+ tagRevision = tagRevision.substring(0, lastDot);
+ }
+ }
+ if (tagRevision.equals(revision)) {
+ int type = isBranch ? CVSTag.BRANCH : CVSTag.VERSION;
+ thisRevisionTags.add(new CVSTag(tagName, type));
+ }
+ }
+ Date date = DateUtil.convertFromLogTime(creationDate);
+ if (currentFile != null) {
+ LogEntry entry = new LogEntry(currentFile, revision, author, date,
+ comment.toString(), fileState, (CVSTag[]) thisRevisionTags.toArray(new CVSTag[0]));
+ addEntry(entry);
+ }
+ state = BEGIN;
+ }
+ return OK;
+ }
+
+ protected void beginFile(ICVSRepositoryLocation location, String fileName) {
+ currentFile = RemoteFile.create(fileName, location);
+ tagNames.clear();
+ tagRevisions.clear();
+ }
+
+ protected void addEntry(LogEntry entry) {
+ listener.handleLogEntryReceived(entry);
+ }
+
+ protected void handleInvalidFileName(ICVSRepositoryLocation location, String badFilePath) {
+ CVSProviderPlugin.log(IStatus.WARNING, "Invalid file path '" + badFilePath + "' received from " + location.toString(), null); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /** branch tags have odd number of segments or have
+ * an even number with a zero as the second last segment
+ * e.g: 1.1.1, 1.26.0.2 are branch revision numbers */
+ protected boolean isBranchTag(String tagName) {
+ // First check if we have an odd number of segments (i.e. even number of dots)
+ int numberOfDots = 0;
+ int lastDot = 0;
+ for (int i = 0; i < tagName.length(); i++) {
+ if (tagName.charAt(i) == '.') {
+ numberOfDots++;
+ lastDot = i;
+ }
+ }
+ if ((numberOfDots % 2) == 0) return true;
+ if (numberOfDots == 1) return false;
+
+ // If not, check if the second lat segment is a zero
+ if (tagName.charAt(lastDot - 1) == '0' && tagName.charAt(lastDot - 2) == '.') return true;
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/UpdateListener.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/UpdateListener.java
new file mode 100644
index 000000000..9e3c548c5
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/client/listeners/UpdateListener.java
@@ -0,0 +1,293 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.core.client.listeners;
+
+import java.util.Map;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.team.internal.ccvs.core.CVSException;
+import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
+import org.eclipse.team.internal.ccvs.core.CVSStatus;
+import org.eclipse.team.internal.ccvs.core.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
+import org.eclipse.team.internal.ccvs.core.Policy;
+import org.eclipse.team.internal.ccvs.core.client.CommandOutputListener;
+import org.eclipse.team.internal.ccvs.core.client.Update;
+import org.eclipse.team.internal.ccvs.core.util.Util;
+
+public class UpdateListener extends CommandOutputListener {
+
+ // Pattern matchers
+ private static ServerMessageLineMatcher MERGED_BINARY_FILE_LINE_1;
+ private static ServerMessageLineMatcher MERGED_BINARY_FILE_LINE_2;
+
+ // Pattern Variables
+ private static final String REVISION_VARIABLE_NAME = "revision"; //$NON-NLS-1$
+ private static final String LOCAL_FILE_PATH_VARIABLE_NAME = "localFilePath"; //$NON-NLS-1$
+ private static final String BACKUP_FILE_VARIABLE_NAME = "backupFile"; //$NON-NLS-1$
+
+ static {
+ try {
+ String line1 = "revision " //$NON-NLS-1$
+ + Util.getVariablePattern(IMessagePatterns.REVISION_PATTERN, REVISION_VARIABLE_NAME)
+ + " from repository is now in " //$NON-NLS-1$
+ + Util.getVariablePattern(IMessagePatterns.FILE_PATH_PATTERN, LOCAL_FILE_PATH_VARIABLE_NAME);
+ MERGED_BINARY_FILE_LINE_1 = new ServerMessageLineMatcher(
+ line1,
+ new String[] {REVISION_VARIABLE_NAME, LOCAL_FILE_PATH_VARIABLE_NAME});
+ String line2 = "file from working directory is now in " //$NON-NLS-1$
+ + Util.getVariablePattern(IMessagePatterns.REVISION_PATTERN, BACKUP_FILE_VARIABLE_NAME);
+ MERGED_BINARY_FILE_LINE_2 = new ServerMessageLineMatcher(
+ line2,
+ new String[] {BACKUP_FILE_VARIABLE_NAME});
+
+ } catch (CVSException e) {
+ // Shouldn't happen
+ CVSProviderPlugin.log(e);
+ }
+ }
+
+ IUpdateMessageListener updateMessageListener;
+ boolean merging = false;
+ boolean mergingBinary = false;
+ String mergedBinaryFileRevision, mergedBinaryFilePath;
+
+ public UpdateListener(IUpdateMessageListener updateMessageListener) {
+ this.updateMessageListener = updateMessageListener;
+ }
+
+ public IStatus messageLine(String line, ICVSRepositoryLocation location, ICVSFolder commandRoot,
+ IProgressMonitor monitor) {
+ mergingBinary = false;
+ if (updateMessageListener == null) return OK;
+ if(line.startsWith("Merging differences")) { //$NON-NLS-1$
+ merging = true;
+ } else if(line.indexOf(' ')==1) {
+ // We have a message that indicates the type of update. The possible messages are
+ // defined by the prefix constants MLP_*.
+ String path = line.substring(2);
+ char changeType = line.charAt(0);
+
+ // calculate change type
+ int type = 0;
+ switch(changeType) {
+ case 'A': type = Update.STATE_ADDED_LOCAL; break; // new file locally that was added but not comitted to server yet
+ case '?': type = Update.STATE_UNKOWN; break; // new file locally but not added to server
+ case 'U': type = Update.STATE_REMOTE_CHANGES; break; // remote changes to an unmodified local file
+ case 'R': type = Update.STATE_DELETED; break; // removed locally but still exists on the server
+ case 'M': type = Update.STATE_MODIFIED; break; // modified locally
+ case 'C': type = Update.STATE_CONFLICT; break; // modified locally and on the server but cannot be auto-merged
+ case 'D': type = Update.STATE_DELETED; break; // deleted locally but still exists on server
+ default: type = Update.STATE_NONE;
+ }
+
+ if (merging) {
+ // If we are merging the modified prefix is used both to show merges and
+ // local changes. We have to detect this case and use a more specific change
+ // type.
+ if (type == Update.STATE_MODIFIED)
+ type = Update.STATE_MERGEABLE_CONFLICT;
+ merging = false;
+ }
+ updateMessageListener.fileInformation(type, commandRoot, path);
+ }
+ return OK;
+ }
+
+ /**
+ * This handler is used by the RemoteResource hierarchy to retrieve E messages
+ * from the CVS server in order to determine the folders contained in a parent folder.
+ *
+ * WARNING: This class parses the message output to determine the state of files in the
+ * repository. Unfortunately, these messages seem to be customizable on a server by server basis.
+ *
+ * Here's a list of responses we expect in various situations:
+ *
+ * Directory exists remotely:
+ * cvs server: Updating folder1/folder2
+ * Directory doesn't exist remotely:
+ * cvs server: skipping directory folder1/folder2
+ * New (or unknown) remote directory
+ * cvs server: New Directory folder1/folder2
+ * File removed remotely
+ * cvs server: folder1/file.ext is no longer in the repository
+ * cvs server: warning: folder1/file.ext is not (any longer) pertinent
+ * Locally added file was added remotely as well
+ * cvs server: conflict: folder/file.ext created independently by second party
+ * File removed locally and modified remotely
+ * cvs server: conflict: removed file.txt was modified by second party
+ * File modified locally but removed remotely
+ * cvs server: conflict: file.txt is modified but no longer in the repository
+ * Ignored Messages
+ * cvs server: cannot open directory ...
+ * cvs server: nothing known about ...
+ * Tag error that really means there are no files in a directory
+ * cvs [server aborted]: no such tag
+ * Merge contained conflicts
+ * rcsmerge: warning: conflicts during merge
+ * Binary file conflict
+ * cvs server: nonmergeable file needs merge
+ * cvs server: revision 1.4 from repository is now in a1/a2/test
+ * cvs server: file from working directory is now in .#test.1.3
+ */
+ public IStatus errorLine(String line, ICVSRepositoryLocation location, ICVSFolder commandRoot,
+ IProgressMonitor monitor) {
+
+ try {
+ // Reset flag globally here because we have to many exit points
+ boolean wasMergingBinary = mergingBinary;
+ mergingBinary = false;
+ String serverMessage = getServerMessage(line, location);
+ if (serverMessage != null) {
+ // Strip the prefix from the line
+ String message = serverMessage;
+ if (message.startsWith("Updating")) { //$NON-NLS-1$
+ if (updateMessageListener != null) {
+ String path = message.substring(9);
+ updateMessageListener.directoryInformation(commandRoot, path, false);
+ }
+ return OK;
+ } else if (message.startsWith("skipping directory")) { //$NON-NLS-1$
+ if (updateMessageListener != null) {
+ String path = message.substring(18).trim();
+ updateMessageListener.directoryDoesNotExist(commandRoot, path);
+ }
+ return OK;
+ } else if (message.startsWith("New directory")) { //$NON-NLS-1$
+ if (updateMessageListener != null) {
+ String path = message.substring(15, message.lastIndexOf('\''));
+ updateMessageListener.directoryInformation(commandRoot, path, true);
+ }
+ return OK;
+ } else if (message.endsWith("is no longer in the repository")) { //$NON-NLS-1$
+ if (updateMessageListener != null) {
+ String filename = message.substring(0, message.length() - 31);
+ filename = stripQuotes(filename);
+ updateMessageListener.fileDoesNotExist(commandRoot, filename);
+ }
+ return OK;
+ } else if (message.startsWith("conflict:")) { //$NON-NLS-1$
+ /*
+ * We can get the following conflict warnings
+ * cvs server: conflict: folder/file.ext created independently by second party
+ * cvs server: conflict: removed file.txt was modified by second party
+ * cvs server: conflict: file.txt is modified but no longer in the repository
+ * If we get the above line, we have conflicting additions or deletions and we can expect a server error.
+ * We still get "C foler/file.ext" so we don't need to do anything else (except in the remotely deleted case)
+ */
+ if (updateMessageListener != null) {
+ if (message.endsWith("is modified but no longer in the repository")) { //$NON-NLS-1$
+ // The "C foler/file.ext" will come after this so if whould be ignored!
+ String filename = message.substring(10, message.length() - 44);
+ filename = stripQuotes(filename);
+ updateMessageListener.fileDoesNotExist(commandRoot, filename);
+ }
+ }
+ return new CVSStatus(CVSStatus.WARNING, CVSStatus.CONFLICT, commandRoot, line);
+ } else if (message.startsWith("warning:")) { //$NON-NLS-1$
+ /*
+ * We can get the following conflict warnings
+ * cvs server: warning: folder1/file.ext is not (any longer) pertinent
+ * If we get the above line, we have local changes to a remotely deleted file.
+ */
+ if (updateMessageListener != null) {
+ if (message.endsWith("is not (any longer) pertinent")) { //$NON-NLS-1$
+ String filename = message.substring(9, message.length() - 30);
+ updateMessageListener.fileDoesNotExist(commandRoot, filename);
+ }
+ }
+ return new CVSStatus(CVSStatus.WARNING, CVSStatus.CONFLICT, commandRoot, line);
+ } else if (message.startsWith("conflicts")) { //$NON-NLS-1$
+ // This line is info only. The server doesn't report an error.
+ return new CVSStatus(IStatus.INFO, CVSStatus.CONFLICT, commandRoot, line);
+ } else if (message.startsWith("nonmergeable file needs merge")) { //$NON-NLS-1$
+ mergingBinary = true;
+ mergedBinaryFileRevision = null;
+ mergedBinaryFilePath = null;
+ return OK;
+ } else if (wasMergingBinary) {
+ Map variables = MERGED_BINARY_FILE_LINE_1.processServerMessage(message);
+ if (variables != null) {
+ mergedBinaryFileRevision = (String)variables.get(REVISION_VARIABLE_NAME);
+ mergedBinaryFilePath = (String)variables.get(LOCAL_FILE_PATH_VARIABLE_NAME);
+ mergingBinary = true;
+ return OK;
+ }
+ variables = MERGED_BINARY_FILE_LINE_2.processServerMessage(message);
+ if (variables != null) {
+ String backupFile = (String)variables.get(BACKUP_FILE_VARIABLE_NAME);
+ try {
+ if (mergedBinaryFileRevision != null && mergedBinaryFilePath != null) {
+ ICVSFile file = commandRoot.getFile(mergedBinaryFilePath);
+ IResource resource = file.getIResource();
+ if (resource != null) {
+ return new CVSStatus(IStatus.ERROR, CVSStatus.UNMEGERED_BINARY_CONFLICT,
+ Policy.bind("UpdateListener.0", new Object[] { //$NON-NLS-1$
+ resource.getFullPath().toString(),
+ mergedBinaryFileRevision,
+ resource.getFullPath().removeLastSegments(1).append(backupFile).toString()}));
+ }
+ }
+ } catch (CVSException e1) {
+ CVSProviderPlugin.log(e1);
+ }
+ return OK;
+ }
+ }
+
+ // Fallthrough case for "cvs server" messages
+ if (!message.startsWith("cannot open directory") //$NON-NLS-1$
+ && !message.startsWith("nothing known about")) { //$NON-NLS-1$
+ return super.errorLine(line, location, commandRoot, monitor);
+ }
+ } else {
+ String serverAbortedMessage = getServerAbortedMessage(line, location);
+ if (serverAbortedMessage != null) {
+ // Strip the prefix from the line
+ String message = serverAbortedMessage;
+ if (message.startsWith("no such tag")) { //$NON-NLS-1$
+ // This is reported from CVS when a tag is used on the update there are no files in the directory
+ // To get the folders, the update request should be re-issued for HEAD
+ return new CVSStatus(CVSStatus.WARNING, CVSStatus.NO_SUCH_TAG, commandRoot, line);
+ } else if (message.startsWith("Numeric join") && message.endsWith("may not contain a date specifier")) { //$NON-NLS-1$ //$NON-NLS-2$
+ // This error indicates a join failed because a date tag was used
+ return super.errorLine(line, location, commandRoot, monitor);
+ } else {
+ return super.errorLine(line, location, commandRoot, monitor);
+ }
+ } else if (line.equals("rcsmerge: warning: conflicts during merge")) { //$NON-NLS-1$
+ // There were conflicts in the merge
+ return new CVSStatus(CVSStatus.WARNING, CVSStatus.CONFLICT, commandRoot, line);
+ }
+ }
+ } catch (StringIndexOutOfBoundsException e) {
+ // Something went wrong in the parsing of the message.
+ // Return a status indicating the problem
+ if (CVSProviderPlugin.getPlugin().isDebugging()) {
+ System.out.println("Error parsing E line: " + line); //$NON-NLS-1$
+ }
+ return new CVSStatus(CVSStatus.ERROR, CVSStatus.ERROR_LINE_PARSE_FAILURE, commandRoot, line);
+ }
+ return super.errorLine(line, location, commandRoot, monitor);
+ }
+
+ private String stripQuotes(String filename) {
+ // CVS version 12 fix - filenames are returned inside quotes
+ // Fixes bug 49056
+ if (filename.startsWith("`") && filename.endsWith("'")) //$NON-NLS-1$ //$NON-NLS-2$
+ filename = filename.substring(1,filename.length()-1);
+ return filename;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
new file mode 100644
index 000000000..66e52a7d8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/messages.properties
@@ -0,0 +1,351 @@
+###############################################################################
+# Copyright (c) 2000, 2004 IBM Corporation and others.
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Common Public License v1.0
+# which accompanies this distribution, and is available at
+# http://www.eclipse.org/legal/cpl-v10.html
+#
+# Contributors:
+# IBM Corporation - initial API and implementation
+###############################################################################
+org.eclipse.team.internal.provider.cvs.CVSException=CVS Error: {0}
+
+ok=ok
+null=null
+internal=An internal error has occurred. Consult the error log for details.
+
+AbstractStructureVisitor.sendingFolder=Processing {0}
+AbstractStructureVisitor.sendingFile=Processing {0}
+AbstractStructureVisitor.noRemote=Unable to determine remote location for resource
+
+AddDeleteMoveListener.deletedResource={0} has been deleted locally
+AddDeleteMoveListener.Error_creating_deletion_marker_1=Error creating deletion marker
+AddDeleteMoveListener.Local_addition_not_under_CVS_control_2=Local addition not under CVS control
+AddDeleteMoveListener.Error_creating_addition_marker_3=Error creating addition marker
+AddDeleteMoveListener.Error_updating_marker_state_4=Error updating marker state
+
+CVSAuthenticationException.detail=Authentication error: {0}
+CVSCommunicationException.io=CVS communication error: {0}
+CVSCommunicationException.interruptCause=The most likely cause of the interrupt is either an intermittent network failure or a communications timeout.
+CVSCommunicationException.interruptSolution=The CVS communications timeout can be adjusted in the Team/CVS preferences.
+CVSCommunicationException.alternateInterruptCause=Another possible cause is the improper configuration of the "ext" connection method.
+CVSCommunicationException.alternateInterruptSolution=The "ext" connection method can be configured on the Team/CVS/EXT Connection Method preference page
+CVSFileException.io=Error accessing CVS file
+CVSDiffException.message=The compared files are different
+CVSStatus.messageWithRoot={0}: {1}
+
+CVSTag.nullName=Name must not be null
+CVSTag.emptyName=Name must not be empty
+CVSTag.beginName=Name must start with a letter
+CVSTag.badCharName=Name must not contain spaces or the characters `$,.:;@|'
+
+CVSWorkspaceRoot.notCVSFolder=The CVS synchronization information for {0} has become corrupt or does not exist
+
+java.io.IOException=I/O exception occurred: {0}
+java.io.EOFException=End of file encountered: {0}
+java.io.FileNotFoundException=File not found: {0}
+java.io.InterruptedIOException=I/O has been interrupted.
+java.net.UnknownHostException=Cannot locate host: {0}
+java.net.ConnectException=Cannot connect to host: {0}
+java.net.SocketException=Socket Exception: {0}
+java.net.NoRouteToHostException={0}
+
+Connection.cannotClose=Cannot close connection
+Connection.readUnestablishedConnection=Failure due to attempt to read from an unestablished connection
+Connection.writeUnestablishedConnection=Failure due to attempt to write to an unestablished connection
+Connection.0=Could not connect to {0}: {1}
+
+PServerConnection.invalidChars=Invalid characters in password
+PServerConnection.hostInvalid=Invalid host
+PServerConnection.loginRefused=Incorrect user name or password
+PServerConnection.invalidUser={0}
+PServerConnection.socket=Cannot connect to host: {0}
+PServerConnection.connectionRefused=Connection refused: {0}
+PServerConnection.stream=Error opening socket connection
+PServerConnection.noResponse=No response from server
+PServerConnection.authenticating=Authenticating using pserver
+
+CVSProviderPlugin.cannotUpdateDescription=Error updating project description
+CVSProviderPlugin.errorDeletingCache=Error occurred deleting cache: {0}
+CVSProviderPlugin.errorCreatingCache=Error occurred creating cache: {0}
+CVSProviderPlugin.unknownStateFileVersion=Could not read CVS state file: unknown version '{0}'.
+
+CVSProvider.exception=Internal error occurred.
+CVSProvider.invalidResource=Resource {0} is not a CVS resource
+CVSProvider.initialImport=Initial import
+CVSProvider.alreadyExists=The specified repository location already exists.
+CVSProvider.rename=An I/O Exception occurred while renaming the state file {0}
+CVSProvider.save=An I/O Exception occurred while saving the state file {0}
+CVSProvider.ioException=I/O Exception occurred on the state file
+CVSProvider.errorSaving=Error saving state
+CVSProvider.errorLoading=Error loading state
+CVSProvider.infoMismatch=Provided CVS information does not match that on disk for project {0}
+
+CVSTeamProvider.noFolderInfo=Project {0} does not contain CVS folder meta-information
+CVSTeamProvider.deconfigureProblem=Error while deconfiguring CVS project {0}
+CVSTeamProvider.initializationFailed=Initialization of CVS for project {0} failed
+CVSTeamProvider.visitError=An error occurred while visiting resource {0}
+CVSTeamProvider.invalidResource=Resource {0} is not a child of project {1}
+CVSTeamProvider.checkinProblems=Problems occurred committing resources to server
+CVSTeamProvider.invalidProjectState=CVS sharing information is missing from project {0}
+CVSTeamProvider.typesDiffer=Error retrieving remote resource tree. Local and remote resource types differ for {0}
+CVSTeamProvider.connectionInfo=Updating connection information for project {0}
+CVSTeamProvider.scrubbingResource=Scrubbing {0}
+CVSTeamProvider.preparingToSetKSubst=Preparing to set keyword substitution mode
+CVSTeamProvider.settingKSubst=Setting keyword substitution mode
+CVSTeamProvider.cleanLineDelimitersException=Exception occurred while cleaning line delimiters
+CVSTeamProvider.changingKeywordComment=*** keyword substitution change ***
+CVSTeamProvider.errorGettingFetchProperty=Could not get "fetch new directory" property for project ''{0}''.
+CVSTeamProvider.errorSettingFetchProperty=Could not set "fetch new directory" property for project ''{0}''.
+CVSTeamProvider.overlappingRemoteFolder=Cannot create linked resource ''{0}'' because a folder of the same name exists remotely.
+CVSTeamProvider.overlappingFileDeletion=Cannot create linked resource ''{0}'' because a deletion for the file of that name has not been committed.
+CVSTeamProvider.errorGettingWatchEdit=Could not get "watch/edit" property for project ''{0}''.
+CVSTeamProvider.errorSettingWatchEdit=Could not set "watch/edit" property for project ''{0}''.
+CVSTeamProvider.errorAddingFileToDiff=An I/O error occurred adding file ''{0}'' to the patch output.
+CVSTeamProvider.updatingFolder=Updating {0}
+
+ResourceDeltaVisitor.visitError=Error while processing resource deltas
+
+ResponseDispatcher.serverError=The CVS server responded with an error (see the CVS console)
+ResponseDispatcher.problemsReported= Errors occurred during the CVS operation
+ResponseDispatcher.receiving=Receiving response
+
+FileProperties.invalidEntryLine=Invalid entry line: {0}
+
+EclipseResource.invalidResourceClass=Two different implementations of ICVSResource used
+
+RemoteResource.invalidResourceClass=Two different implementations of ICVSResource used
+RemoteResource.invalidOperation=Invalid operation performed on remote resource
+RemoteFolder.errorFetchingRevisions=Error fetching file revision numbers
+RemoteFolder.invalidChild=Resource {0} is not a child of folder {1}
+RemoteFolder.errorFetchingRevisions=Error fetching file revisions
+RemoteFolder.errorFetchingMembers=One or more error occurred fetching the members of a remote folder
+RemoteFolder.doesNotExist=Folder {0} does not exist remotely or you do not have permission to access it
+
+RemoteFile.noContentsReceived=No contents received from server for {0}
+RemoteFile.errorRetrievingFromCache=Error occurred retrieving cached contents: {0}
+
+RemoteFolderTreeBuilder.buildingBase=Collecting local synchronization information
+RemoteFolderTreeBuilder.0=Resource {0} is no longer mapped to CVS.
+RemoteFolderTreeBuilder.receivingDelta=Receiving delta for {0}
+RemoteFolderTreeBuilder.receivingRevision=Receiving revision for {0}
+RemoteFolderTreeBuilder.missingParent=An error has occurred processing file ''{0} {1}''
+RemoteFolderTreeBuild.folderDeletedFromServer=Folder ''{0}'' has been deleted from the server.
+
+ReplaceWithBaseVisitor.replacing=Replacing ''{0}''
+
+Session.badInt="Malformed file transmission received"
+Session.receiving=Receiving file: {0}
+Session.sending=Sending file: {0}
+Session.transfer={0} ({1}K of {2}K bytes)
+Session.transferNoSize={0}
+Session.calculatingCompressedSize=Calculating compressed size: {0}
+Session.dot_2=dot
+Session.0=cvs client: {0}
+
+Command.receivingResponses=Receiving server response
+Command.warnings=The following warnings were reported while performing the "cvs {0}" command.
+Command.serverError=The server reported an error while performing the "cvs {0}" command.
+Command.noMoreInfoAvailable=The server did not provide any additional information.
+Command.add=add
+Command.admin=admin
+Command.annotate=annotate
+Command.co=checkout
+Command.ci=commit
+Command.diff=diff
+Command.editors=editors
+Command.import=import
+Command.log=log
+Command.remove=remove
+Command.status=status
+Command.tag=tag
+Command.rtag=rtag
+Command.update=update
+Command.version=version
+Command.rdiff=rdiff
+Command.valid-requests=valid-requests
+Command.expand-modules=expand-modules
+Command.unsupportedResponse=Unknown response received from cvs server: {0} {1}
+Command.argumentNotManaged=Argument {0} is not managed
+Command.invalidTag=HEAD is not a valid tag
+Command.noOpenSession=The CVS command cannot be issued because there is no connection available
+Command.seriousServerError=The server reported an error: {0}
+
+Commit.syncInfoMissing=The commit operation succeeded. However, committed file ''{0}'' no longer has CVS synchronization information.
+Commit.timestampReset=The modification timestamp was changed for ''{0}'' but the contents match that of the server. The timstamp has been reset.
+
+Diff.serverError=The server reported an error while performing the "cvs diff" command which may only indicate that a difference exists.
+
+Tag.notVersionOrBranchError=Error applying tag: the tag provided is not a version or branch tag.
+
+DefaultHandler.connectionClosed=The connection to the server has been closed
+ModTimeHandler.invalidFormat=The server modification time {0} is in an unknown format
+Updated.numberFormat=Server did not send length of the file
+UpdateListener.0=An unmergable conflict has occurred for binary file {0}. Revision {1} has been loaded and overwritten local changes have been saved in file {2}
+UnsupportedHandler.message=Unsupported response received from server
+RemovedHandler.invalid=Invalid removed response received from CVS server for {0}
+RemovedHandler.0=An error occurred removing resource {0}
+CheckInHandler.checkedIn= Receiving confirmation for file {0}.
+
+KSubstOption.-kb.short=Binary
+KSubstOption.-kb.long=Binary (-kb)
+KSubstOption.-ko.short=ASCII -ko
+KSubstOption.-ko.long=ASCII without keyword substitution (-ko)
+KSubstOption.-kkv.short=ASCII -kkv
+KSubstOption.-kkv.long=ASCII with keyword expansion (-kkv)
+KSubstOption.-kkvl.short=ASCII -kkvl
+KSubstOption.-kkvl.long=ASCII with keyword expansion and locker (-kkvl)
+KSubstOption.-kv.short=ASCII -kv
+KSubstOption.-kv.long=ASCII with keyword replacement (-kv)
+KSubstOption.-kk.short=ASCII -kk
+KSubstOption.-kk.long=ASCII with keyword compression (-kk)
+KSubstOption.unknown.short=Unknown {0}
+KSubstOption.unknown.long=Unknown ({0})
+
+AdminKSubstListener.expectedRCSFile=Expected RCS file {0} to end in ',v'
+AdminKSubstListener.commandRootNotManaged=Local root for this command is not managed
+AdminKSubstListener.expectedChildOfCommandRoot=Expected RCS file {0} to be a child of remote root for this command {1}
+AdminKSubstListener.couldNotSetResourceSyncInfo=Could not set resource sync info for {0}: {1}
+
+CVSRepositoryLocation.nullLocation=Location must not be null
+CVSRepositoryLocation.emptyLocation=Location must not be empty
+CVSRepositoryLocation.endWhitespace=Location must not end with whitespace
+CVSRepositoryLocation.locationForm=Location must have form ':methodname:[user[:password]@]host:[port]/path/to/cvsroot'
+CVSRepositoryLocation.startOfLocation=Location must start with a connection method name enclosed in colons
+CVSRepositoryLocation.methods=Only the following methods are supported: {0}
+CVSRepositoryLocation.parsingMethod=Error in connection method specification
+CVSRepositoryLocation.parsingUser=Error in user name specification
+CVSRepositoryLocation.parsingPassword=Error in password specification
+CVSRepositoryLocation.parsingHost=Error in host specification
+CVSRepositoryLocation.parsingPort=Error in port specification
+CVSRepositoryLocation.parsingRoot=Error in repository root directory specification
+CVSRepositoryLocation.invalidFormat=Invalid CVS repository location format: {0}
+CVSRepositoryLocation.authenticationCanceled=Authentication canceled by user
+CVSRepositoryLocation.errorCaching=Error occurred while saving password for {0}
+CVSRepositoryLocation.openingConnection=Opening connection to {0}
+CVSRepositoryLocation.usernameRequired=A username is required to make a connection
+
+ProjectDescriptionContentHandler.xml=Error parsing project description file
+
+Util.invalidResource=Resource {1} is not relative to root {0}
+Util.timeout=A timeout occurred connecting to host {0}
+Util.processTimeout=A timeout occurred executing command ''{0}''
+Util.truncatedPath=...{0}
+
+ResourceSyncInfo.malformedSyncBytes=Malformed entry line bytes encountered: {0}
+Synchronizer.reload=Examining {0}
+Checking_out_from_CVS..._5=Checking out from CVS...
+FileSystemSynchronizer_Error_loading_from_CVS/Entries_file_1=Error loading from CVS/Entries file
+FileSystemSynchronizer_Error_loading_from_.cvsignore_file_2=Error loading from .cvsignore file
+FileSystemSynchronizer_Error_loading_from_CVS/Root,Repository_files_3=Error loading from CVS/Root,Repository files
+FileSystemSynchronizer_Error_reloading_sync_information_5=Error reloading sync information
+Malformed_entry_line___11=Malformed entry line:
+Malformed_entry_line,_missing_name___12=Malformed entry line, missing name:
+Malformed_entry_line,_missing_revision___13=Malformed entry line, missing revision:
+FolderSyncInfo_Maleformed_root_4=Malformed root
+SyncFileUtil_Error_writing_to_Entries.log_48=Error writing to Entries.log
+SyncFileUtil_Cannot_close_Entries.log_49=Cannot close Entries.log
+SyncFileUtil_Error_reloading_sync_information_58=Error reloading sync information
+SyncFileUtil_Error_writing_to_.cvsignore_61=Error writing to .cvsignore
+SyncFileUtil_Cannot_close_.cvsignore_62=Cannot close .cvsignore
+SyncFileWriter.baseNotAvailable=Could not restore the base contents of ''{0}'' from the local cache.
+BaseRevInfo.malformedEntryLine=Malformed entry line ''{0}'' for base revision information file.
+
+FileModificationValidator.isReadOnly=File is Read Only.
+
+EXTServerConnection.invalidPort=A port cannot be specified for the ext connection method.
+EXTServerConnection.varsNotSet=Cannot run external ext program because CVS_RSH and CVS_SERVER variables are not initialized.
+EXTServerConnection.ioError=Error starting external connection program: {0}. Ensure that the path is correct and that you can connect manually using this program.
+
+CVSRemoteSyncElement.rootDiffers=Error mapping local folder {0} to repository {1}. It is already managed by repository {2}.
+CVSRemoteSyncElement.repositoryDiffers=Error mapping local folder {0} to remote folder {1}. It is already mapped to {2}.
+Util.Internal_error,_resource_does_not_start_with_root_3=Internal error, resource does not start with root
+
+CVSProvider.Scrubbing_local_project_1=Scrubbing local project
+CVSProvider.Scrubbing_projects_1=Scrubbing projects
+CVSProvider.Creating_projects_2=Creating projects
+
+EclipseFile_Problem_deleting_resource=Problem deleting resource: {0}. {1}
+EclipseFile_Problem_accessing_resource=Problem accessing resource: {0}. {1} Perform a Refresh.
+EclipseFile_Problem_creating_resource=Problem creating resource: {0}. {1}
+EclipseFile_Problem_writing_resource=Problem writing resource ''{0}''. {1}
+EclipseFolder_problem_creating=Problem creating folder: {0}. {1}
+EclipseFolder.isModifiedProgress=Determining if {0} has outgoing changes...
+
+EclipseSynchronizer.UpdatingSyncEndOperation=Updating CVS synchronization information...
+EclipseSynchronizer.UpdatingSyncEndOperationCancelled=Operation cancelled: updating CVS synchronization information...
+EclipseSynchronizer.NotifyingListeners=Notifying of CVS changes...
+EclipseSynchronizer.ErrorSettingFolderSync=Cannot set folder sync info on {0}
+EclipseSynchronizer.ErrorSettingResourceSync=Cannot set resource sync info on {0}
+EclipseSynchronizer.ErrorSettingIgnorePattern=Cannot set ignored pattern on {0}
+EclipseSynchronizer.ErrorCommitting=Errors saving CVS synchronization information to disk. Please fix the problems listed below and then update the affected resources from the CVS repository.
+EclipseSynchronizer.folderSyncInfoMissing=CVS synchronization information could not be found for folder ''{0}''
+EclipseSynchronizer.workspaceClosedForResource=Invalid attempt to modify the sync info for resource ''{0}''
+
+SynchrnoizerSyncInfoCache.failedToSetSyncBytes=Could not change sync info for ''{0}'' from ''{1}'' to ''{2}'' because the workspace is locked.
+
+SyncFileChangeListener.errorSettingTeamPrivateFlag=Error setting team-private flag on resource
+
+RemoteFile.getContents=Retrieving remote file contents
+RemoteFile.getLogEntries=Retrieving log entries
+RemoteFolder.exists=Checking if resource exists remotely
+RemoteFolder.getMembers=Retrieving children of remote folder
+RemoteModule.getRemoteModules=Retrieving remote modules
+RemoteModule.invalidDefinition=Invalid module definition ''{0}'' received from ''{1}''.
+
+PruneFolderVisitor.caseVariantsExist=The following resources could not be created.
+PruneFolderVisitor.caseVariantExists=The resource ''{0}'' could not be created because another resource exists whose path differs only by case.
+
+Version.unsupportedVersion=Host ''{0}'' is running unsupported CVS version {1}. Although most functionality works, use version 1.11.1p1 or later for full support.
+Version.unknownVersionFormat=Host ''{0}'' is running ''{1}'' which is an unknown version to the workbench. Although most functionality may work, use version 1.11.1p1 or later for full support.
+Version.versionNotValidRequest=Unable to determine server version. Host ''{0}'' does not support the ''cvs version'' command. Although most functionality works, use version 1.11.1p1 or later for full support.
+
+LogListener.invalidRevisionFormat=Invalid revision format ''{1}'' for tag ''{0}''.
+RemoteFile.Could_not_cache_remote_contents_to_disk._Caching_remote_file_in_memory_instead._1=Could not cache remote contents to disk. Caching remote file in memory instead. Exception follows.
+
+NotifyInfo.MalformedLine=Invalid Notify format: ''{0}''
+NotifyInfo.MalformedNotificationType=Invalid notification type in line: ''{0}''
+NotifyInfo.MalformedNotifyDate=Invalid date format in line: ''{0}''
+
+ResourceSynchronizer.missingParentBytesOnGet=Synchronization bytes are missing for parent of resource ''{1}'' in synchronization partner ''{0}'' on get.
+ResourceSynchronizer.missingParentBytesOnSet=Synchronization bytes are missing for parent of resource ''{1}'' in synchronization partner ''{0}'' on set.
+CVSAnnotateBlock.4=lines
+CVSAnnotateBlock.5=line
+CVSAnnotateBlock.6={0} {1} ({2} {3})
+CVSMergeSubscriber.2=CVS Merge ''{0} to {1}''
+CVSMergeSubscriber.4=Shows the the differences between a merge source and the workspace.
+CVSMergeSubscriber.13=Error restoring merge subscriber: {0} is an invalid save context.
+CVSMergeSubscriber.19=Error restoring merge subscriber {0}: There are no roots in the save context.
+CVSMergeSubscriber.21=Ignoring root resource {0} because it does not exist in the current workspace.
+CVSMergeSubscriber.22=Error restoring merge subscriber {0}: There are no existing roots in the save context.
+CVSProviderPlugin.20=CVS Workspace
+CVSProviderPlugin.21=Synchronizes the CVS managed resources in your workspace with their associated remote location
+CVSRevisionNumberCompareCriteria.1=Comparing revision numbers
+ReentrantLock.9=An error occurred writting CVS synchronization information to disk. Some information may be lost.
+CRLFDetectInputStream.0=CVS file {0} either contains invalid line endings on the server (CR/LF instead of just LF) or is a binary file that is not marked as -kb.
+SynchronizerSyncInfoCache.0=Synchronization information could not be cached for {0}. The only negative effect of this may be decreased performance.
+DeferredResourceChangeHandler.0=Reconciling CVS state changes
+DeferredResourceChangeHandler.1=Errors occured handling ignore file (.cvsignore) changes. Some resources may not be decorated properly.
+CVSWorkspaceRoot.11=The parent folder of managed file {0} does not have sync info associated with it.
+RemoveEntryHandler.2=Remove-entry received and ignored from CVS server for existing file {0}.
+ServerMessageLineMatcher.5=Variable in template is not of the correct format: {0}
+ServerMessageLineMatcher.6=There are additional groups above those defining variables in template: {0}
+ServerMessageLineMatcher.7=Expected variable {0} in {1} but it is not present.
+CVSSyncInfo.7=Invalid attempt to make file {0} in-sync. This operation can only be sed on folders.
+CVSSyncInfo.8=Invalid attempt to make outgoing resource {0} in-sync. This operation only applies to incoming or conflicting changes.
+CVSSyncInfo.9=Cannot make {0} in-sync because its parent is not under CVS control.
+CVSSyncInfo.10=Cannot make {0} in-sync because there is no corresponding remote.
+CVSCompareSubscriber.2=CVS Compare ''{0}''
+CVSCompareSubscriber.3=Shows the differences between a tag and the workspace.
+CompareDiffListener.11=Unsupported message sequence received while comparing using CVS diff command
+CompareDiffListener.12=Unknown message format received while comparing using CVS diff command: {0}
+AnnotateListener.3=Skipping binary file
+AnnotateListener.4=Cannot annotate a binary file.
+CVSWorkspaceSubscriber.1=Calculating synchronization state for {0}
+CVSWorkspaceSubscriber.2=An error occurred calculating the synchronization state for {0}: {1}
+KnownRepositories.0=Error restoring CVS repositories
+CVSRepositoryLocation.73=Error clearing preferences for CVS repository location {0}
+CVSRepositoryLocation.74=Error retrieving preferences for CVS repository location {0}
+CVSRepositoryLocation.75=Error flushing preferences for CVS repository location {0}
+SyncFileWriter.0=An invalid entry was found in the CVS/Entries file for folder {0}. The entry has been ignored.
+ResponseHandler.0=Could not create resource {0}: {1}
diff --git a/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java
new file mode 100644
index 000000000..c75025842
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.core/src/org/eclipse/team/internal/ccvs/core/util/CVSDateFormatter.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.core.util;
+
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Utility class for converting timestamps used in Entry file lines. The format
+ * required in the Entry file is ISO C asctime() function (Sun Apr 7 01:29:26 1996).
+ * <p>
+ * To be compatible with asctime(), the day field in the entryline format is
+ * padded with a space and not a zero. Most other CVS clients use string comparison
+ * for timestamps based on the result of the C function asctime().
+ * </p>
+ */
+public class CVSDateFormatter {
+
+ private static final String ENTRYLINE_FORMAT = "E MMM dd HH:mm:ss yyyy"; //$NON-NLS-1$
+ private static final String SERVER_FORMAT = "dd MMM yyyy HH:mm:ss";//$NON-NLS-1$
+ private static final int ENTRYLINE_TENS_DAY_OFFSET = 8;
+
+ private static final SimpleDateFormat serverFormat = new SimpleDateFormat(SERVER_FORMAT, Locale.US);
+ private static SimpleDateFormat entryLineFormat = new SimpleDateFormat(ENTRYLINE_FORMAT, Locale.US);
+
+ static {
+ entryLineFormat.setTimeZone(TimeZone.getTimeZone("GMT")); //$NON-NLS-1$
+ }
+ static synchronized public Date serverStampToDate(String text) throws ParseException {
+ serverFormat.setTimeZone(getTimeZone(text));
+ Date date = serverFormat.parse(text);
+ return date;
+ }
+
+ static synchronized public String dateToServerStamp(Date date) {
+ serverFormat.setTimeZone(TimeZone.getTimeZone("GMT"));//$NON-NLS-1$
+ return serverFormat.format(date) + " -0000"; //$NON-NLS-1$
+ }
+
+ static synchronized public Date entryLineToDate(String text) throws ParseException {
+ try {
+ if (text.charAt(ENTRYLINE_TENS_DAY_OFFSET) == ' ') {
+ StringBuffer buf = new StringBuffer(text);
+ buf.setCharAt(ENTRYLINE_TENS_DAY_OFFSET, '0');
+ text = buf.toString();
+ }
+ } catch (StringIndexOutOfBoundsException e) {
+ throw new ParseException(e.getMessage(), ENTRYLINE_TENS_DAY_OFFSET);
+ }
+ return entryLineFormat.parse(text);
+ }
+
+ static synchronized public String dateToEntryLine(Date date) {
+ if (date == null) return ""; //$NON-NLS-1$
+ String passOne = entryLineFormat.format(date);
+ if (passOne.charAt(ENTRYLINE_TENS_DAY_OFFSET) != '0') return passOne;
+ StringBuffer passTwo = new StringBuffer(passOne);
+ passTwo.setCharAt(ENTRYLINE_TENS_DAY_OFFSET, ' ');
+ return passTwo.toString();
+ }
+
+ static synchronized public String dateToNotifyServer(Date date) {
+ serverFormat.setTimeZone(TimeZone.getTimeZone("GMT"));//$NON-NLS-1$
+ return serverFormat.format(date) + " GMT"; //$NON-NLS-1$
+ }
+
+ /*
+ * Converts timezone text from date string from CVS server and
+ * returns a timezone representing the received timezone.
+ * Timezone string is of the following format: [-|+]MMSS
+ */
+ static private TimeZone getTimeZone(String dateFromServer) {
+ if (dateFromServer.lastIndexOf("0000") != -1) //$NON-NLS-1$
+ return TimeZone.getTimeZone("GMT");//$NON-NLS-1$
+ String tz = null;
+ StringBuffer resultTz = new StringBuffer("GMT");//$NON-NLS-1$
+ if (dateFromServer.indexOf("-") != -1) {//$NON-NLS-1$
+ resultTz.append("-");//$NON-NLS-1$
+ tz = dateFromServer.substring(dateFromServer.indexOf("-"));//$NON-NLS-1$
+ } else if (dateFromServer.indexOf("+") != -1) {//$NON-NLS-1$
+ resultTz.append('+');
+ tz = dateFromServer.substring(dateFromServer.indexOf("+"));//$NON-NLS-1$
+ }
+ try {
+ if(tz!=null) {
+ resultTz.append(tz.substring(1, 3) /*hours*/ + ":" + tz.substring(3, 5) /*minutes*/);//$NON-NLS-1$
+ return TimeZone.getTimeZone(resultTz.toString());
+ }
+ } catch(IndexOutOfBoundsException e) {
+ return TimeZone.getTimeZone("GMT");//$NON-NLS-1$
+ }
+ return TimeZone.getTimeZone("GMT");//$NON-NLS-1$
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/BranchPromptDialog.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/BranchPromptDialog.java
new file mode 100644
index 000000000..4a052014a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/BranchPromptDialog.java
@@ -0,0 +1,231 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.ui.IHelpContextIds;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.wizards.CVSWizardPage;
+import org.eclipse.team.internal.ui.dialogs.DetailsDialog;
+import org.eclipse.ui.help.WorkbenchHelp;
+
+public class BranchPromptDialog extends DetailsDialog {
+
+ private String branchTag = ""; //$NON-NLS-1$
+ private String versionTag= ""; //$NON-NLS-1$
+ private String versionName= ""; //$NON-NLS-1$
+
+ private boolean allStickyResources;
+ private boolean update;
+
+ private Text versionText;
+ private Text branchText;
+
+ private static final int TAG_AREA_HEIGHT_HINT = 200;
+
+ // widgets;
+ private TagSource tagSource;
+ private TagSelectionArea tagArea;
+ private final IResource[] resources;
+
+ public BranchPromptDialog(Shell parentShell, String title, IResource[] resources, boolean allResourcesSticky, String versionName) {
+ super(parentShell, title);
+ this.resources = resources;
+ this.tagSource = TagSource.create(resources);
+ this.allStickyResources = allResourcesSticky;
+ this.versionName = versionName;
+ }
+
+ /**
+ * @see DetailsDialog#createMainDialogArea(Composite)
+ */
+ protected void createMainDialogArea(Composite composite) {
+ // create message
+ Label label = new Label(composite, SWT.WRAP);
+ String message;
+ if(allStickyResources) {
+ message = Policy.bind("BranchWizardPage.pageDescriptionVersion"); //$NON-NLS-1$
+ } else {
+ message = Policy.bind("BranchWizardPage.pageDescription"); //$NON-NLS-1$
+ }
+ label.setText(message);
+ GridData data = new GridData(
+ GridData.GRAB_HORIZONTAL |
+ GridData.HORIZONTAL_ALIGN_FILL |
+ GridData.VERTICAL_ALIGN_CENTER);
+ data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
+ label.setLayoutData(data);
+
+ CVSWizardPage.createLabel(composite, Policy.bind("BranchWizardPage.branchName")); //$NON-NLS-1$
+ branchText = CVSWizardPage.createTextField(composite);
+ branchText.addListener(SWT.Modify, new Listener() {
+ public void handleEvent(Event event) {
+ branchTag = branchText.getText();
+ updateEnablements();
+ updateVersionName(branchTag);
+ }
+ });
+ addBranchContentAssist();
+
+ final Button check = new Button(composite, SWT.CHECK);
+ data = new GridData();
+ data.horizontalSpan = 2;
+ check.setLayoutData(data);
+ check.setText(Policy.bind("BranchWizardPage.startWorking")); //$NON-NLS-1$
+ check.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ update = check.getSelection();
+ }
+ });
+ check.setSelection(true);
+ update = true;
+
+ label = new Label(composite, SWT.WRAP);
+ label.setText(Policy.bind("BranchWizardPage.specifyVersion")); //$NON-NLS-1$
+ data = new GridData(
+ GridData.GRAB_HORIZONTAL |
+ GridData.HORIZONTAL_ALIGN_FILL |
+ GridData.VERTICAL_ALIGN_CENTER);
+ data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);
+ data.horizontalSpan = 2;
+ label.setLayoutData(data);
+
+ CVSWizardPage.createLabel(composite, Policy.bind("BranchWizardPage.versionName")); //$NON-NLS-1$
+ versionText = CVSWizardPage.createTextField(composite);
+ versionText.addListener(SWT.Modify, new Listener() {
+ public void handleEvent(Event event) {
+ versionTag = versionText.getText();
+ updateEnablements();
+ }
+ });
+
+ if(allStickyResources) {
+ versionText.setEditable(false);
+ versionText.setText(versionName);
+ }
+
+ // F1 Help
+ WorkbenchHelp.setHelp(composite, IHelpContextIds.BRANCH_DIALOG);
+ Dialog.applyDialogFont(composite);
+ branchText.setFocus();
+ }
+
+ private void addBranchContentAssist() {
+ TagSource projectTagSource = LocalProjectTagSource.create(getSeedProject());
+ if (projectTagSource != null)
+ TagContentAssistProcessor.createContentAssistant(branchText, projectTagSource, TagSelectionArea.INCLUDE_BRANCHES);
+ }
+
+ private IProject getSeedProject() {
+ return resources[0].getProject();
+ }
+
+ /**
+ * Updates version name
+ */
+ protected void updateVersionName(String branchName) {
+ if(versionText!=null && !allStickyResources) {
+ versionText.setText(Policy.bind("BranchWizardPage.versionPrefix") + branchName); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * @see DetailsDialog#createDropDownDialogArea(Composite)
+ */
+ protected Composite createDropDownDialogArea(Composite parent) {
+
+ // create a composite with standard margins and spacing
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+ layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+ layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+ layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
+ composite.setLayout(layout);
+ GridData gridData = new GridData(GridData.FILL_BOTH);
+ gridData.heightHint = TAG_AREA_HEIGHT_HINT;
+ composite.setLayoutData(gridData);
+
+ tagArea = new TagSelectionArea(getShell(), tagSource, TagSelectionArea.INCLUDE_VERSIONS | TagSelectionArea.INCLUDE_BRANCHES, null);
+ tagArea.setTagAreaLabel(Policy.bind("BranchWizardPage.existingVersionsAndBranches")); //$NON-NLS-1$
+ tagArea.setIncludeFilterInputArea(false);
+ tagArea.createArea(composite);
+
+ return composite;
+ }
+
+ /**
+ * Validates branch and version names
+ */
+ protected void updateEnablements() {
+ String message = null;
+
+ if (branchTag.length() == 0) {
+ message = ""; //$NON-NLS-1$
+ } else {
+ IStatus status = CVSTag.validateTagName(branchTag);
+ if (!status.isOK()) {
+ message = Policy.bind("BranchWizard.branchNameWarning", status.getMessage()); //$NON-NLS-1$
+ } else {
+ if(versionText!=null) {
+ status = CVSTag.validateTagName(versionText.getText());
+ if (!status.isOK()) {
+ message = Policy.bind("BranchWizard.versionNameWarning", status.getMessage()); //$NON-NLS-1$
+ } else {
+ if(versionTag.length() != 0 && versionTag.equals(branchTag)) {
+ message = Policy.bind("BranchWizard.branchAndVersionMustBeDifferent"); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+ }
+ setPageComplete(message == null);
+ setErrorMessage(message);
+ }
+
+ /**
+ * Returns the branch tag name
+ */
+ public String getBranchTagName() {
+ return branchTag;
+ }
+
+ /**
+ * Returns the version tag name
+ */
+ public String getVersionTagName() {
+ return versionTag;
+ }
+
+ /**
+ * Returns the state of the update checkbox
+ */
+ public boolean getUpdate() {
+ return update;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ui.dialogs.DetailsDialog#isMainGrabVertical()
+ */
+ protected boolean isMainGrabVertical() {
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFileElement.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFileElement.java
new file mode 100644
index 000000000..4b4e05700
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFileElement.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.ICVSFile;
+import org.eclipse.team.internal.ccvs.core.ICVSResource;
+import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
+import org.eclipse.team.internal.ccvs.ui.model.CVSResourceElement;
+import org.eclipse.ui.PlatformUI;
+
+public class CVSFileElement extends CVSResourceElement {
+
+ private ICVSFile file;
+
+ public CVSFileElement(ICVSFile file) {
+ this.file = file;
+ }
+
+ /**
+ * Initial implementation: return null;
+ */
+ public Object[] fetchChildren(Object o, IProgressMonitor monitor) {
+ return new Object[0];
+ }
+ /**
+ * Initial implementation: return null.
+ */
+ public ImageDescriptor getImageDescriptor(Object object) {
+ return PlatformUI.getWorkbench().getEditorRegistry().getImageDescriptor(file.getName());
+ }
+ /**
+ * Initial implementation: return the file's name and version
+ */
+ public String getLabel(Object o) {
+ try {
+ ResourceSyncInfo info = file.getSyncInfo();
+ if(info!=null) {
+ return file.getName() + " " + info.getRevision(); //$NON-NLS-1$
+ } else {
+ return file.getName();
+ }
+ } catch (TeamException e) {
+ handle(null, null, e);
+ return null;
+ }
+ }
+ /**
+ * @see IWorkbenchAdapter#getParent(Object)
+ */
+ public Object getParent(Object o) {
+ return null;
+ }
+
+ public ICVSFile getCVSFile() {
+ return file;
+ }
+ /**
+ * @see CVSResourceElement#getCVSResource()
+ */
+ public ICVSResource getCVSResource() {
+ return file;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFolderElement.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFolderElement.java
new file mode 100644
index 000000000..865b95801
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/CVSFolderElement.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.ui.model.CVSResourceElement;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+public class CVSFolderElement extends CVSResourceElement {
+
+ private ICVSFolder folder;
+ private boolean includeUnmanaged;
+
+ public CVSFolderElement(ICVSFolder folder, boolean includeUnmanaged) {
+ this.folder = folder;
+ this.includeUnmanaged = includeUnmanaged;
+ }
+
+ /**
+ * Returns CVSResourceElement instances
+ */
+ public Object[] fetchChildren(Object o, IProgressMonitor monitor) throws TeamException {
+ ICVSResource[] children = folder.fetchChildren(monitor);
+ CVSResourceElement[] elements = new CVSResourceElement[children.length];
+ for (int i = 0; i < children.length; i++) {
+ ICVSResource resource = children[i];
+ if(resource.isFolder()) {
+ elements[i] = new CVSFolderElement((ICVSFolder)resource, includeUnmanaged);
+ } else {
+ elements[i] = new CVSFileElement((ICVSFile)resource);
+ }
+ }
+ return elements;
+ }
+
+ /**
+ * @see org.eclipse.team.internal.ccvs.ui.model.CVSModelElement#isRemoteElement()
+ */
+ public boolean isRemoteElement() {
+ return true;
+ }
+
+ /**
+ * Overridden to append the version name to remote folders which
+ * have version tags and are top-level folders.
+ */
+ public String getLabel(Object o) {
+ return folder.getName();
+ }
+
+ public ImageDescriptor getImageDescriptor(Object object) {
+ return PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_OBJ_FOLDER);
+ }
+
+ /**
+ * @see IWorkbenchAdapter#getParent(Object)
+ */
+ public Object getParent(Object o) {
+ return new CVSFolderElement(folder.getParent(), includeUnmanaged);
+ }
+
+ /**
+ * @see CVSResourceElement#getCVSResource()
+ */
+ public ICVSResource getCVSResource() {
+ return folder ;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/FilteredTagList.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/FilteredTagList.java
new file mode 100644
index 000000000..a8266373d
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/FilteredTagList.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.core.util.StringMatcher;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+/**
+ * Workbench model element that returns a filtered list of tags
+ */
+public class FilteredTagList implements IWorkbenchAdapter, IAdaptable {
+
+ private final TagSource tagSource;
+ private final int[] types;
+ private StringMatcher matcher;
+
+ public FilteredTagList(TagSource tagSource, int[] types) {
+ this.tagSource = tagSource;
+ this.types = types;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)
+ */
+ public Object[] getChildren(Object o) {
+ CVSTag[] tags = getTags();
+ List filtered = new ArrayList();
+ for (int i = 0; i < tags.length; i++) {
+ CVSTag tag = tags[i];
+ if (select(tag)) {
+ filtered.add(new TagElement(this, tag));
+ }
+ }
+ return filtered.toArray(new Object[filtered.size()]);
+ }
+
+ private boolean select(CVSTag tag) {
+ if (matcher == null) return true;
+ return matcher.match(tag.getName());
+ }
+
+ private CVSTag[] getTags() {
+ return tagSource.getTags(types);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)
+ */
+ public ImageDescriptor getImageDescriptor(Object object) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)
+ */
+ public String getLabel(Object o) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)
+ */
+ public Object getParent(Object o) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class) return this;
+ return null;
+ }
+
+ public void setPattern(String pattern) {
+ if (!pattern.endsWith("*")) { //$NON-NLS-1$
+ pattern += "*"; //$NON-NLS-1$
+ }
+ matcher = new StringMatcher(pattern, true, false);
+ }
+
+ public CVSTag[] getMatchingTags() {
+ CVSTag[] tags = getTags();
+ List filtered = new ArrayList();
+ for (int i = 0; i < tags.length; i++) {
+ CVSTag tag = tags[i];
+ if (select(tag)) {
+ filtered.add(tag);
+ }
+ }
+ return (CVSTag[])filtered.toArray(new CVSTag[filtered.size()]);
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/LocalProjectTagSource.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/LocalProjectTagSource.java
new file mode 100644
index 000000000..1ffa1c7f1
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/LocalProjectTagSource.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+
+/**
+ * Tag source that gets its tags from the projects exist in the workspace
+ * and are mapped to the same repository as the seed project
+ */
+public class LocalProjectTagSource extends TagSource {
+
+ public static TagSource create(IProject seedProject) {
+ try {
+ ICVSRemoteFolder seedFolder = ((ICVSRemoteFolder)CVSWorkspaceRoot.getRemoteResourceFor(seedProject));
+ ICVSRemoteFolder[] remoteFolders = getProjectRemoteFolders(seedFolder.getRepository());
+ if (remoteFolders.length == 1) {
+ // There are no other projects to get tags from so return null
+ return null;
+ }
+ return new LocalProjectTagSource(seedFolder, remoteFolders);
+ } catch (CVSException e) {
+ // Log and return null
+ CVSUIPlugin.log(e);
+ return null;
+ }
+ }
+
+ private ICVSRemoteFolder seedFolder;
+ private ICVSRemoteFolder[] remoteFolders;
+
+ private LocalProjectTagSource(ICVSRemoteFolder seedFolder, ICVSRemoteFolder[] remoteFolders) {
+ this.seedFolder = seedFolder;
+ this.remoteFolders = remoteFolders;
+ }
+
+ /*
+ * Return the list of remote folders for the projects in the workspace mapped to the given repository
+ */
+ private static ICVSRemoteFolder[] getProjectRemoteFolders(ICVSRepositoryLocation repository) {
+ IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
+ List result = new ArrayList();
+ for (int i = 0; i < projects.length; i++) {
+ IProject project = projects[i];
+ try {
+ if (project.isAccessible() && RepositoryProvider.isShared(project)) {
+ ICVSRemoteFolder remote = (ICVSRemoteFolder)CVSWorkspaceRoot.getRemoteResourceFor(project);
+ if (remote != null && remote.getRepository().equals(repository)) {
+ result.add(remote);
+ }
+ }
+ } catch (CVSException e) {
+ // Log and continue
+ CVSUIPlugin.log(e);
+ }
+ }
+ return (ICVSRemoteFolder[]) result.toArray(new ICVSRemoteFolder[result.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#refresh(boolean, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public CVSTag[] refresh(boolean bestEffort, IProgressMonitor monitor) throws TeamException {
+ // This tag source should not be refreshed
+ return new CVSTag[0];
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getLocation()
+ */
+ public ICVSRepositoryLocation getLocation() {
+ return seedFolder.getRepository();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getShortDescription()
+ */
+ public String getShortDescription() {
+ return Policy.bind("LocalProjectTagSource.0", Integer.toString(remoteFolders.length)); //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#commit(org.eclipse.team.internal.ccvs.core.CVSTag[], boolean, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void commit(CVSTag[] tags, boolean replace, IProgressMonitor monitor) throws CVSException {
+ // Does not commit tags
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getCVSResources()
+ */
+ public ICVSResource[] getCVSResources() {
+ return remoteFolders;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getTags(int)
+ */
+ public CVSTag[] getTags(int type) {
+ if (type == CVSTag.HEAD || type == BASE) {
+ return super.getTags(type);
+ }
+ // Accumulate the tags for all folders
+ Set allTags = new HashSet();
+ for (int i = 0; i < remoteFolders.length; i++) {
+ ICVSRemoteFolder folder = remoteFolders[i];
+ CVSTag[] tags = SingleFolderTagSource.getTags(folder, type);
+ allTags.addAll(Arrays.asList(tags));
+ }
+ // Exclude the tags for the seedFolder
+ CVSTag[] tags = SingleFolderTagSource.getTags(seedFolder, type);
+ allTags.removeAll(Arrays.asList(tags));
+ return (CVSTag[]) allTags.toArray(new CVSTag[allTags.size()]);
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/MultiFolderTagSource.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/MultiFolderTagSource.java
new file mode 100644
index 000000000..ac8f949a3
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/MultiFolderTagSource.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import org.eclipse.team.internal.ccvs.core.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.ICVSResource;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+
+/**
+ * A tag source for multiple folders.
+ *
+ * TODO: Temporarily a subclass of single folder until I
+ * can figure out how to handle the multi-folder case.
+ */
+public class MultiFolderTagSource extends SingleFolderTagSource {
+
+ private final ICVSFolder[] folders;
+
+ /* package */ MultiFolderTagSource(ICVSFolder[] folders) {
+ super(folders[0]);
+ this.folders = folders;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.SingleFolderTagSource#getShortDescription()
+ */
+ public String getShortDescription() {
+ return Policy.bind("MultiFolderTagSource.0", Integer.toString(folders.length)); //$NON-NLS-1$
+ }
+
+ /**
+ * Return the folders of this tag source
+ * @return
+ */
+ public ICVSFolder[] getFolders() {
+ return folders;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.SingleFolderTagSource#getCVSResources()
+ */
+ public ICVSResource[] getCVSResources() {
+ return folders;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFileTagSource.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFileTagSource.java
new file mode 100644
index 000000000..30cd23307
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFileTagSource.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.repo.RepositoryManager;
+
+/**
+ * A tag source for a single ICVSFile
+ */
+public class SingleFileTagSource extends TagSource {
+
+ public static CVSTag[] fetchTagsFor(ICVSFile file, IProgressMonitor monitor) throws TeamException {
+ Set tagSet = new HashSet();
+ ILogEntry[] entries = file.getLogEntries(monitor);
+ for (int j = 0; j < entries.length; j++) {
+ CVSTag[] tags = entries[j].getTags();
+ for (int k = 0; k < tags.length; k++) {
+ tagSet.add(tags[k]);
+ }
+ }
+ return (CVSTag[])tagSet.toArray(new CVSTag[tagSet.size()]);
+ }
+
+ private ICVSFile file;
+ private TagSource parentFolderTagSource;
+
+ /* package */ /**
+ *
+ */
+ public SingleFileTagSource(ICVSFile file) {
+ this.file = file;
+ parentFolderTagSource = TagSource.create(new ICVSResource[] { file.getParent() });
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getTags(int)
+ */
+ public CVSTag[] getTags(int type) {
+ return parentFolderTagSource.getTags(type);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#refresh(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public CVSTag[] refresh(boolean bestEffort, IProgressMonitor monitor) throws TeamException {
+ CVSTag[] tags = fetchTagsFor(file, monitor);
+ commit(tags, false, monitor);
+ fireChange();
+ return tags;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getLocation()
+ */
+ public ICVSRepositoryLocation getLocation() {
+ RepositoryManager mgr = CVSUIPlugin.getPlugin().getRepositoryManager();
+ ICVSRepositoryLocation location = mgr.getRepositoryLocationFor(file);
+ return location;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getShortDescription()
+ */
+ public String getShortDescription() {
+ return file.getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#commit(org.eclipse.team.internal.ccvs.core.CVSTag[], boolean, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void commit(CVSTag[] tags, boolean replace, IProgressMonitor monitor) throws CVSException {
+ parentFolderTagSource.commit(tags, replace, monitor);
+ fireChange();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getCVSResources()
+ */
+ public ICVSResource[] getCVSResources() {
+ return new ICVSResource[] { file };
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFolderTagSource.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFolderTagSource.java
new file mode 100644
index 000000000..1c6bb08a8
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/SingleFolderTagSource.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.repo.RepositoryManager;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * A tag source that returns the tags associated with a single remote folder
+ */
+public class SingleFolderTagSource extends TagSource {
+
+ public static CVSTag[] getTags(ICVSFolder folder, int type) {
+ if (type == CVSTag.HEAD)
+ return new CVSTag[] { CVSTag.DEFAULT } ;
+ return CVSUIPlugin.getPlugin().getRepositoryManager().getKnownTags(folder, type);
+ }
+
+ private ICVSFolder folder;
+
+ /* package */ SingleFolderTagSource(ICVSFolder folder) {
+ this.folder = folder;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#getTags(int)
+ */
+ public CVSTag[] getTags(int type) {
+ if (type == CVSTag.HEAD || type == BASE) {
+ return super.getTags(type);
+ }
+ return getTags(getFolder(), type);
+ }
+
+ /**
+ * Return the folder the tags are obtained from
+ * @return the folder the tags are obtained from
+ */
+ public ICVSFolder getFolder() {
+ return folder;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#refresh(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public CVSTag[] refresh(boolean bestEffort, IProgressMonitor monitor) throws TeamException {
+ CVSTag[] tags = CVSUIPlugin.getPlugin().getRepositoryManager().refreshDefinedTags(getFolder(), bestEffort /* recurse */, true /* notify */, monitor);
+ fireChange();
+ return tags;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#getLocation()
+ */
+ public ICVSRepositoryLocation getLocation() {
+ RepositoryManager mgr = CVSUIPlugin.getPlugin().getRepositoryManager();
+ ICVSRepositoryLocation location = mgr.getRepositoryLocationFor(getFolder());
+ return location;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#getShortDescription()
+ */
+ public String getShortDescription() {
+ return getFolder().getName();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#commit(org.eclipse.team.internal.ccvs.core.CVSTag[], boolean, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void commit(final CVSTag[] tags, final boolean replace, IProgressMonitor monitor) throws CVSException {
+ try {
+ final RepositoryManager manager = CVSUIPlugin.getPlugin().getRepositoryManager();
+ manager.run(new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ try {
+ ICVSFolder folder = getFolder();
+ if (replace) {
+ CVSTag[] oldTags = manager.getKnownTags(folder);
+ manager.removeTags(folder, oldTags);
+ }
+ manager.addTags(folder, tags);
+ } catch (CVSException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+ }, monitor);
+ } catch (InvocationTargetException e) {
+ throw CVSException.wrapException(e);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ fireChange();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getCVSResources()
+ */
+ public ICVSResource[] getCVSResources() {
+ final ICVSResource[][] resources = new ICVSResource[][] { null };
+ try {
+ getRunnableContext().run(true, true, new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ try {
+ resources[0] = folder.fetchChildren(monitor);
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ monitor.done();
+ }
+ }
+ });
+ return resources[0];
+ } catch (InvocationTargetException e) {
+ CVSUIPlugin.log(CVSException.wrapException(e));
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ return new ICVSResource[] { folder };
+ }
+
+ private IRunnableContext getRunnableContext() {
+ return PlatformUI.getWorkbench().getProgressService();
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagAsVersionDialog.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagAsVersionDialog.java
new file mode 100644
index 000000000..3a59a7026
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagAsVersionDialog.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.ui.IHelpContextIds;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.operations.ITagOperation;
+import org.eclipse.team.internal.ui.dialogs.DetailsDialog;
+import org.eclipse.ui.help.WorkbenchHelp;
+
+public class TagAsVersionDialog extends DetailsDialog {
+
+ private static final int TAG_AREA_HEIGHT_HINT = 200;
+
+ private ITagOperation operation;
+
+ private Text tagText;
+ private Button moveTagButton;
+
+ private String tagName = ""; //$NON-NLS-1$
+ private boolean moveTag = false;
+
+ private TagSource tagSource;
+
+ private TagSelectionArea tagArea;
+
+ public TagAsVersionDialog(Shell parentShell, String title, ITagOperation operation) {
+ super(parentShell, title);
+ this.tagSource = operation.getTagSource();
+ this.operation = operation;
+ }
+
+ /**
+ * @see DetailsDialog#createMainDialogArea(Composite)
+ */
+ protected void createMainDialogArea(Composite parent) {
+ // create message
+ Label label = new Label(parent, SWT.WRAP);
+ label.setText(Policy.bind("TagAction.enterTag")); //$NON-NLS-1$
+ GridData data = new GridData(
+ GridData.GRAB_HORIZONTAL |
+ GridData.HORIZONTAL_ALIGN_FILL |
+ GridData.VERTICAL_ALIGN_CENTER);
+ data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH);;
+ label.setLayoutData(data);
+
+ tagText = new Text(parent, SWT.SINGLE | SWT.BORDER);
+ tagText.setLayoutData(new GridData(
+ GridData.GRAB_HORIZONTAL |
+ GridData.HORIZONTAL_ALIGN_FILL));
+ tagText.addModifyListener(
+ new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ tagName = tagText.getText();
+ updateEnablements();
+ }
+ }
+ );
+
+ moveTagButton = new Button(parent, SWT.CHECK);
+ moveTagButton.setText(Policy.bind("TagAction.moveTag")); //$NON-NLS-1$
+ moveTagButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+
+ moveTagButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ moveTag = moveTagButton.getSelection();
+ }
+ });
+
+ // Add F1 help
+ WorkbenchHelp.setHelp(parent, IHelpContextIds.TAG_AS_VERSION_DIALOG);
+ Dialog.applyDialogFont(parent);
+ }
+
+ public boolean shouldMoveTag() {
+ return moveTag;
+ }
+
+ /**
+ * @see DetailsDialog#createDropDownDialogArea(Composite)
+ */
+ protected Composite createDropDownDialogArea(Composite parent) {
+ // create a composite with standard margins and spacing
+ Composite composite = new Composite(parent, SWT.NONE);
+ GridLayout layout = new GridLayout();
+ layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+ layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+ layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
+ layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
+ composite.setLayout(layout);
+ GridData gridData = new GridData(GridData.FILL_BOTH);
+ gridData.heightHint = TAG_AREA_HEIGHT_HINT;
+ composite.setLayoutData(gridData);
+
+ tagArea = new TagSelectionArea(getShell(), tagSource, TagSelectionArea.INCLUDE_VERSIONS, null);
+ tagArea.setTagAreaLabel(Policy.bind("TagAction.existingVersions")); //$NON-NLS-1$
+ tagArea.setIncludeFilterInputArea(false);
+ tagArea.createArea(composite);
+ tagArea.addPropertyChangeListener(new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(TagSelectionArea.SELECTED_TAG)) {
+ CVSTag tag = tagArea.getSelection();
+ if (tag != null) {
+ tagText.setText(tag.getName());
+ }
+ } else if (event.getProperty().equals(TagSelectionArea.OPEN_SELECTED_TAG)) {
+ CVSTag tag = tagArea.getSelection();
+ if (tag != null) {
+ tagText.setText(tag.getName());
+ okPressed();
+ }
+ }
+ }
+ });
+ return composite;
+ }
+
+ /**
+ * Validates tag name
+ */
+ protected void updateEnablements() {
+ String message = null;
+ if(tagName.length() == 0) {
+ message = ""; //$NON-NLS-1$
+ } else {
+ IStatus status = CVSTag.validateTagName(tagName);
+ if (!status.isOK()) {
+ message = status.getMessage();
+ }
+ }
+ setPageComplete(message == null);
+ setErrorMessage(message);
+ if (tagArea != null) {
+ tagArea.setFilter(tagName);
+ }
+ }
+
+ /**
+ * Returns the tag name entered into this dialog
+ */
+ public String getTagName() {
+ return tagName;
+ }
+
+ /**
+ * @return
+ */
+ public ITagOperation getOperation() {
+ operation.setTag(new CVSTag(tagName, CVSTag.VERSION));
+ if (moveTag) {
+ operation.moveTag();
+ }
+ return operation;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ui.dialogs.DetailsDialog#isMainGrabVertical()
+ */
+ protected boolean isMainGrabVertical() {
+ return false;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagConfigurationDialog.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagConfigurationDialog.java
new file mode 100644
index 000000000..0f3a41d3e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagConfigurationDialog.java
@@ -0,0 +1,763 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.jface.dialogs.*;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.ui.*;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.model.RemoteContentProvider;
+import org.eclipse.team.internal.ccvs.ui.repo.NewDateTagAction;
+import org.eclipse.team.internal.ccvs.ui.repo.RepositoryManager;
+import org.eclipse.team.internal.ccvs.ui.tags.TagSourceWorkbenchAdapter.ProjectElementSorter;
+import org.eclipse.ui.help.WorkbenchHelp;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+
+/**
+ * Allows configuration of the CVS tags that are shown within the workbench.
+ */
+public class TagConfigurationDialog extends Dialog {
+
+ // show the resource contained within the roots
+ private TreeViewer cvsResourceTree;
+
+ // shows the tags found on the selected resources
+ private CheckboxTableViewer cvsTagTree;
+
+ // shows the defined tags for the given root
+ private TreeViewer cvsDefinedTagsTree;
+
+ // remember the root element in the defined tags tree
+ private TagSourceWorkbenchAdapter cvsDefinedTagsRootElement;
+
+ // list of auto-refresh files
+ private org.eclipse.swt.widgets.List autoRefreshFileList;
+
+ // enable selecting auto-refresh files
+ private boolean allowSettingAutoRefreshFiles = true;
+
+ // preference keys
+ private final String ALLOWREFRESH_WIDTH_KEY = "AllowRefreshWidth"; //$NON-NLS-1$
+ private final String ALLOWREFRESH_HEIGHT_KEY = "AllowRefreshHeight"; //$NON-NLS-1$
+ private final String NOREFRESH_WIDTH_KEY = "NoRefreshWidth"; //$NON-NLS-1$
+ private final String NOREFRESH_HEIGHT_KEY = "NoRefreshHeight"; //$NON-NLS-1$
+
+ // buttons
+ private Button addSelectedTagsButton;
+ private Button addSelectedFilesButton;
+ private Button removeFileButton;
+ private Button removeTagButton;
+
+ // dialogs settings that are persistent between workbench sessions
+ private IDialogSettings settings;
+
+ private final TagSource tagSource;
+
+ private final TagSourceWrapper wrappedTagSource;
+
+ class FileSorter extends ViewerSorter {
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ boolean oneIsFile = e1 instanceof CVSFileElement;
+ boolean twoIsFile = e2 instanceof CVSFileElement;
+ if (oneIsFile != twoIsFile) {
+ return oneIsFile ? 1 : -1;
+ }
+ return super.compare(viewer, e1, e2);
+ }
+ }
+
+ /*
+ * Create a tag source that cahces the added and removed tags
+ * so that the changes can be propogated to the repository
+ * manager when OK is pressed
+ */
+ class TagSourceWrapper extends TagSource {
+
+ private final TagSource tagSource;
+ private final List branches = new ArrayList();
+ private final List versions = new ArrayList();
+ private final List dates = new ArrayList();
+
+ public TagSourceWrapper(TagSource tagSource) {
+ this.tagSource = tagSource;
+ branches.addAll(Arrays.asList(tagSource.getTags(CVSTag.BRANCH)));
+ versions.addAll(Arrays.asList(tagSource.getTags(CVSTag.VERSION)));
+ dates.addAll(Arrays.asList(tagSource.getTags(CVSTag.DATE)));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#getTags(int)
+ */
+ public CVSTag[] getTags(int type) {
+ if (type == CVSTag.HEAD || type == BASE) {
+ return super.getTags(type);
+ }
+ List list = getTagList(type);
+ if (list != null)
+ return (CVSTag[]) list.toArray(new CVSTag[list.size()]);
+ return tagSource.getTags(type);
+ }
+
+ private List getTagList(int type) {
+ switch (type) {
+ case CVSTag.VERSION:
+ return versions;
+ case CVSTag.BRANCH:
+ return branches;
+ case CVSTag.DATE:
+ return dates;
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#refresh(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public CVSTag[] refresh(boolean bestEffort, IProgressMonitor monitor) throws TeamException {
+ // The wrapper is never refreshed
+ return new CVSTag[0];
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#getLocation()
+ */
+ public ICVSRepositoryLocation getLocation() {
+ return tagSource.getLocation();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#getShortDescription()
+ */
+ public String getShortDescription() {
+ return tagSource.getShortDescription();
+ }
+
+ public void remove(CVSTag[] tags) {
+ for (int i = 0; i < tags.length; i++) {
+ CVSTag tag = tags[i];
+ List list = getTagList(tag.getType());
+ if (list != null)
+ list.remove(tag);
+ }
+ }
+
+ public void add(CVSTag[] tags) {
+ for (int i = 0; i < tags.length; i++) {
+ CVSTag tag = tags[i];
+ List list = getTagList(tag.getType());
+ if (list != null)
+ list.add(tag);
+ }
+ }
+
+ public void removeAll() {
+ versions.clear();
+ branches.clear();
+ dates.clear();
+ }
+
+ /**
+ * Remember the state that has been accumulated
+ * @param monitor
+ * @throws CVSException
+ */
+ public void commit(IProgressMonitor monitor) throws CVSException {
+ tagSource.commit(getTags(new int[] { CVSTag.VERSION, CVSTag.BRANCH, CVSTag.DATE }), true /* replace */, monitor);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.merge.TagSource#commit(org.eclipse.team.internal.ccvs.core.CVSTag[], boolean, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void commit(CVSTag[] tags, boolean replace, IProgressMonitor monitor) throws CVSException {
+ // Not invoked
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ccvs.ui.tags.TagSource#getCVSResources()
+ */
+ public ICVSResource[] getCVSResources() {
+ return tagSource.getCVSResources();
+ }
+ }
+
+ public TagConfigurationDialog(Shell shell, TagSource tagSource) {
+ super(shell);
+ this.tagSource = tagSource;
+ wrappedTagSource = new TagSourceWrapper(tagSource);
+ setShellStyle(SWT.CLOSE|SWT.RESIZE|SWT.APPLICATION_MODAL);
+ allowSettingAutoRefreshFiles = getSingleFolder(tagSource, false) != null;
+ IDialogSettings workbenchSettings = CVSUIPlugin.getPlugin().getDialogSettings();
+ this.settings = workbenchSettings.getSection("TagConfigurationDialog");//$NON-NLS-1$
+ if (settings == null) {
+ this.settings = workbenchSettings.addNewSection("TagConfigurationDialog");//$NON-NLS-1$
+ }
+ }
+
+ /**
+ * @see Window#configureShell(Shell)
+ */
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText(Policy.bind("TagConfigurationDialog.1", tagSource.getShortDescription())); //$NON-NLS-1$
+ }
+
+ /**
+ * @see Dialog#createDialogArea(Composite)
+ */
+ protected Control createDialogArea(Composite parent) {
+ Composite shell = new Composite(parent, SWT.NONE);
+ GridData data = new GridData (GridData.FILL_BOTH);
+ shell.setLayoutData(data);
+ GridLayout gridLayout = new GridLayout();
+ gridLayout.numColumns = 2;
+ gridLayout.makeColumnsEqualWidth = true;
+ gridLayout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
+ gridLayout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
+ shell.setLayout (gridLayout);
+
+ Composite comp = new Composite(shell, SWT.NULL);
+ gridLayout = new GridLayout();
+ gridLayout.numColumns = 1;
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ comp.setLayout(gridLayout);
+ comp.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Label cvsResourceTreeLabel = new Label(comp, SWT.NONE);
+ cvsResourceTreeLabel.setText(Policy.bind("TagConfigurationDialog.5")); //$NON-NLS-1$
+ data = new GridData();
+ data.horizontalSpan = 1;
+ cvsResourceTreeLabel.setLayoutData(data);
+
+ Tree tree = new Tree(comp, SWT.BORDER | SWT.MULTI);
+ cvsResourceTree = new TreeViewer (tree);
+ cvsResourceTree.setContentProvider(new RemoteContentProvider());
+ cvsResourceTree.setLabelProvider(new WorkbenchLabelProvider());
+ data = new GridData (GridData.FILL_BOTH);
+ data.heightHint = 150;
+ data.horizontalSpan = 1;
+ cvsResourceTree.getTree().setLayoutData(data);
+ cvsResourceTree.setInput(TagSourceResourceAdapter.getViewerInput(tagSource));
+ cvsResourceTree.setSorter(new FileSorter());
+ cvsResourceTree.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ updateShownTags();
+ updateEnablements();
+ }
+ });
+
+ comp = new Composite(shell, SWT.NULL);
+ gridLayout = new GridLayout();
+ gridLayout.numColumns = 1;
+ gridLayout.marginWidth = 0;
+ gridLayout.marginHeight = 0;
+ comp.setLayout(gridLayout);
+ comp.setLayoutData(new GridData(GridData.FILL_BOTH));
+
+ Label cvsTagTreeLabel = new Label(comp, SWT.NONE);
+ cvsTagTreeLabel.setText(Policy.bind("TagConfigurationDialog.6")); //$NON-NLS-1$
+ data = new GridData();
+ data.horizontalSpan = 1;
+ cvsTagTreeLabel.setLayoutData(data);
+
+ final Table table = new Table(comp, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.CHECK);
+ data = new GridData(GridData.FILL_BOTH);
+ data.heightHint = 150;
+ data.horizontalSpan = 1;
+ table.setLayoutData(data);
+ cvsTagTree = new CheckboxTableViewer(table);
+ cvsTagTree.setContentProvider(new WorkbenchContentProvider());
+ cvsTagTree.setLabelProvider(new WorkbenchLabelProvider());
+ cvsTagTree.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ updateEnablements();
+ }
+ });
+
+ Composite selectComp = new Composite(comp, SWT.NONE);
+ GridLayout selectLayout = new GridLayout(2, true);
+ selectLayout.marginHeight = selectLayout.marginWidth = 0;
+ selectComp.setLayout(selectLayout);
+ selectComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ Button selectAllButton = new Button(selectComp, SWT.PUSH);
+ selectAllButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ selectAllButton.setText(Policy.bind("ReleaseCommentDialog.selectAll")); //$NON-NLS-1$
+ selectAllButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ int nItems = table.getItemCount();
+ for (int j=0; j<nItems; j++)
+ table.getItem(j).setChecked(true);
+ }
+ });
+ Button deselectAllButton = new Button(selectComp, SWT.PUSH);
+ deselectAllButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ deselectAllButton.setText(Policy.bind("ReleaseCommentDialog.deselectAll")); //$NON-NLS-1$
+ deselectAllButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ int nItems = table.getItemCount();
+ for (int j=0; j<nItems; j++)
+ table.getItem(j).setChecked(false);
+ }
+ });
+
+ cvsTagTree.setSorter(new ViewerSorter() {
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ if (!(e1 instanceof TagElement) || !(e2 instanceof TagElement)) return super.compare(viewer, e1, e2);
+ CVSTag tag1 = ((TagElement)e1).getTag();
+ CVSTag tag2 = ((TagElement)e2).getTag();
+ int type1 = tag1.getType();
+ int type2 = tag2.getType();
+ if (type1 != type2) {
+ return type1 - type2;
+ }
+ // Sort in reverse order so larger numbered versions are at the top
+ return -tag1.compareTo(tag2);
+ }
+ });
+
+ Composite rememberedTags = new Composite(shell, SWT.NONE);
+ data = new GridData (GridData.FILL_BOTH);
+ data.horizontalSpan = 2;
+ rememberedTags.setLayoutData(data);
+ gridLayout = new GridLayout();
+ gridLayout.numColumns = 2;
+ gridLayout.marginHeight = 0;
+ gridLayout.marginWidth = 0;
+ rememberedTags.setLayout (gridLayout);
+
+ Label rememberedTagsLabel = new Label (rememberedTags, SWT.NONE);
+ rememberedTagsLabel.setText (Policy.bind("TagConfigurationDialog.7")); //$NON-NLS-1$
+ data = new GridData ();
+ data.horizontalSpan = 2;
+ rememberedTagsLabel.setLayoutData (data);
+
+ tree = new Tree(rememberedTags, SWT.BORDER | SWT.MULTI);
+ cvsDefinedTagsTree = new TreeViewer (tree);
+ cvsDefinedTagsTree.setContentProvider(new WorkbenchContentProvider());
+ cvsDefinedTagsTree.setLabelProvider(new WorkbenchLabelProvider());
+ data = new GridData (GridData.FILL_BOTH);
+ data.heightHint = 100;
+ data.horizontalAlignment = GridData.FILL;
+ data.grabExcessHorizontalSpace = true;
+ cvsDefinedTagsTree.getTree().setLayoutData(data);
+ cvsDefinedTagsRootElement = new TagSourceWorkbenchAdapter(wrappedTagSource, TagSourceWorkbenchAdapter.INCLUDE_BRANCHES | TagSourceWorkbenchAdapter.INCLUDE_VERSIONS |TagSourceWorkbenchAdapter.INCLUDE_DATES);
+ cvsDefinedTagsTree.setInput(cvsDefinedTagsRootElement);
+ cvsDefinedTagsTree.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ updateEnablements();
+ }
+ });
+ cvsDefinedTagsTree.setSorter(new ProjectElementSorter());
+
+ Composite buttonComposite = new Composite(rememberedTags, SWT.NONE);
+ data = new GridData ();
+ data.verticalAlignment = GridData.BEGINNING;
+ buttonComposite.setLayoutData(data);
+ gridLayout = new GridLayout();
+ gridLayout.marginHeight = 0;
+ gridLayout.marginWidth = 0;
+ buttonComposite.setLayout (gridLayout);
+
+ addSelectedTagsButton = new Button (buttonComposite, SWT.PUSH);
+ addSelectedTagsButton.setText (Policy.bind("TagConfigurationDialog.8")); //$NON-NLS-1$
+ data = getStandardButtonData(addSelectedTagsButton);
+ data.horizontalAlignment = GridData.FILL;
+ addSelectedTagsButton.setLayoutData(data);
+ addSelectedTagsButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ rememberCheckedTags();
+ updateShownTags();
+ updateEnablements();
+ }
+ });
+ Button addDatesButton = new Button(buttonComposite, SWT.PUSH);
+ addDatesButton.setText(Policy.bind("TagConfigurationDialog.0")); //$NON-NLS-1$
+ data = getStandardButtonData(addDatesButton);
+ data.horizontalAlignment = GridData.FILL;
+ addDatesButton.setLayoutData(data);
+ addDatesButton.addListener(SWT.Selection, new Listener(){
+ public void handleEvent(Event event){
+ CVSTag dateTag = NewDateTagAction.getDateTag(getShell(), tagSource.getLocation());
+ addDateTagsSelected(dateTag);
+ updateShownTags();
+ updateEnablements();
+ }
+ });
+ removeTagButton = new Button (buttonComposite, SWT.PUSH);
+ removeTagButton.setText (Policy.bind("TagConfigurationDialog.9")); //$NON-NLS-1$
+ data = getStandardButtonData(removeTagButton);
+ data.horizontalAlignment = GridData.FILL;
+ removeTagButton.setLayoutData(data);
+ removeTagButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ deleteSelected();
+ updateShownTags();
+ updateEnablements();
+ }
+ });
+
+ Button removeAllTags = new Button (buttonComposite, SWT.PUSH);
+ removeAllTags.setText (Policy.bind("TagConfigurationDialog.10")); //$NON-NLS-1$
+ data = getStandardButtonData(removeAllTags);
+ data.horizontalAlignment = GridData.FILL;
+ removeAllTags.setLayoutData(data);
+ removeAllTags.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ removeAllKnownTags();
+ updateShownTags();
+ updateEnablements();
+ }
+ });
+
+ if(allowSettingAutoRefreshFiles) {
+ Label explanation = new Label(rememberedTags, SWT.WRAP);
+ explanation.setText(Policy.bind("TagConfigurationDialog.11")); //$NON-NLS-1$
+ data = new GridData ();
+ data.horizontalSpan = 2;
+ //data.widthHint = 300;
+ explanation.setLayoutData(data);
+
+ autoRefreshFileList = new org.eclipse.swt.widgets.List(rememberedTags, SWT.BORDER | SWT.MULTI);
+ data = new GridData ();
+ data.heightHint = 45;
+ data.horizontalAlignment = GridData.FILL;
+ data.grabExcessHorizontalSpace = true;
+ autoRefreshFileList.setLayoutData(data);
+ try {
+ autoRefreshFileList.setItems(CVSUIPlugin.getPlugin().getRepositoryManager().getAutoRefreshFiles(getSingleFolder(tagSource, false)));
+ } catch (CVSException e) {
+ autoRefreshFileList.setItems(new String[0]);
+ CVSUIPlugin.log(e);
+ }
+ autoRefreshFileList.addSelectionListener(new SelectionListener() {
+ public void widgetSelected(SelectionEvent e) {
+ updateEnablements();
+ }
+ public void widgetDefaultSelected(SelectionEvent e) {
+ updateEnablements();
+ }
+ });
+
+ Composite buttonComposite2 = new Composite(rememberedTags, SWT.NONE);
+ data = new GridData ();
+ data.verticalAlignment = GridData.BEGINNING;
+ buttonComposite2.setLayoutData(data);
+ gridLayout = new GridLayout();
+ gridLayout.marginHeight = 0;
+ gridLayout.marginWidth = 0;
+ buttonComposite2.setLayout (gridLayout);
+
+ addSelectedFilesButton = new Button (buttonComposite2, SWT.PUSH);
+ addSelectedFilesButton.setText (Policy.bind("TagConfigurationDialog.12")); //$NON-NLS-1$
+ data = getStandardButtonData(addSelectedFilesButton);
+ data.horizontalAlignment = GridData.FILL;
+ addSelectedFilesButton.setLayoutData(data);
+ addSelectedFilesButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ addSelectionToAutoRefreshList();
+ }
+ });
+
+ removeFileButton = new Button (buttonComposite2, SWT.PUSH);
+ removeFileButton.setText (Policy.bind("TagConfigurationDialog.13")); //$NON-NLS-1$
+ data = getStandardButtonData(removeFileButton);
+ data.horizontalAlignment = GridData.FILL;
+ removeFileButton.setLayoutData(data);
+ removeFileButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ String[] selected = autoRefreshFileList.getSelection();
+ for (int i = 0; i < selected.length; i++) {
+ autoRefreshFileList.remove(selected[i]);
+ autoRefreshFileList.setFocus();
+ }
+ }
+ });
+ WorkbenchHelp.setHelp(autoRefreshFileList, IHelpContextIds.TAG_CONFIGURATION_REFRESHLIST);
+ }
+
+ Label seperator = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL);
+ data = new GridData (GridData.FILL_BOTH);
+ data.horizontalSpan = 2;
+ seperator.setLayoutData(data);
+
+ WorkbenchHelp.setHelp(shell, IHelpContextIds.TAG_CONFIGURATION_OVERVIEW);
+
+ updateEnablements();
+ Dialog.applyDialogFont(parent);
+ return shell;
+ }
+
+ private void updateShownTags() {
+ final CVSFileElement[] filesSelection = getSelectedFiles();
+ final Set tags = new HashSet();
+ if(filesSelection.length!=0) {
+ try {
+ CVSUIPlugin.runWithProgress(getShell(), true /*cancelable*/, new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ monitor.beginTask(Policy.bind("TagConfigurationDialog.22"), filesSelection.length); //$NON-NLS-1$
+ try {
+ for (int i = 0; i < filesSelection.length; i++) {
+ ICVSFile file = filesSelection[i].getCVSFile();
+ tags.addAll(Arrays.asList(getTagsFor(file, Policy.subMonitorFor(monitor, 1))));
+ }
+ } catch (TeamException e) {
+ // ignore the exception
+ } finally {
+ monitor.done();
+ }
+ }
+ });
+ } catch (InterruptedException e) {
+ // operation cancelled
+ } catch (InvocationTargetException e) {
+ // can't happen since we're ignoring all possible exceptions
+ }
+ cvsTagTree.getTable().removeAll();
+ for (Iterator it = tags.iterator(); it.hasNext();) {
+ CVSTag tag = (CVSTag) it.next();
+ List knownTags = new ArrayList();
+ knownTags.addAll(Arrays.asList(wrappedTagSource.getTags(new int[] { CVSTag.VERSION, CVSTag.BRANCH, CVSTag.DATE })));
+ if(!knownTags.contains(tag)) {
+ TagElement tagElem = new TagElement(tag);
+ cvsTagTree.add(tagElem);
+ cvsTagTree.setChecked(tagElem, true);
+ }
+ }
+ }
+ }
+
+ private CVSFileElement[] getSelectedFiles() {
+ IStructuredSelection selection = (IStructuredSelection)cvsResourceTree.getSelection();
+ if (!selection.isEmpty()) {
+ final List filesSelection = new ArrayList();
+ Iterator it = selection.iterator();
+ while(it.hasNext()) {
+ Object o = it.next();
+ if(o instanceof CVSFileElement) {
+ filesSelection.add(o);
+ }
+ }
+ return (CVSFileElement[]) filesSelection.toArray(new CVSFileElement[filesSelection.size()]);
+ }
+ return new CVSFileElement[0];
+ }
+
+ private void addSelectionToAutoRefreshList() {
+ IStructuredSelection selection = (IStructuredSelection)cvsResourceTree.getSelection();
+ if (!selection.isEmpty()) {
+ final List filesSelection = new ArrayList();
+ Iterator it = selection.iterator();
+ while(it.hasNext()) {
+ Object o = it.next();
+ if(o instanceof CVSFileElement) {
+ filesSelection.add(o);
+ }
+ }
+ if(!filesSelection.isEmpty()) {
+ for (it = filesSelection.iterator(); it.hasNext();) {
+ try {
+ ICVSFile file = ((CVSFileElement)it.next()).getCVSFile();
+ ICVSFolder fileParent = file.getParent();
+ String filePath = new Path(null, fileParent.getFolderSyncInfo().getRepository())
+ .append(file.getRelativePath(fileParent)).toString();
+ if(autoRefreshFileList.indexOf(filePath)==-1) {
+ autoRefreshFileList.add(filePath);
+ }
+ } catch(CVSException e) {
+ CVSUIPlugin.openError(getShell(), null, null, e);
+ }
+ }
+ }
+ }
+ }
+
+ private CVSTag[] getTagsFor(ICVSFile file, IProgressMonitor monitor) throws TeamException {
+ return SingleFileTagSource.fetchTagsFor(file, monitor);
+ }
+
+ private void rememberCheckedTags() {
+ Object[] checked = cvsTagTree.getCheckedElements();
+ List tagsToAdd = new ArrayList();
+ for (int i = 0; i < checked.length; i++) {
+ CVSTag tag = ((TagElement)checked[i]).getTag();
+ tagsToAdd.add(tag);
+ }
+ if (!tagsToAdd.isEmpty()) {
+ wrappedTagSource.add((CVSTag[]) tagsToAdd.toArray(new CVSTag[tagsToAdd.size()]));
+ cvsDefinedTagsTree.refresh();
+ }
+ }
+
+ private void deleteSelected() {
+ IStructuredSelection selection = (IStructuredSelection)cvsDefinedTagsTree.getSelection();
+ List tagsToRemove = new ArrayList();
+ if (!selection.isEmpty()) {
+ Iterator it = selection.iterator();
+ while(it.hasNext()) {
+ Object o = it.next();
+ if(o instanceof TagElement) {
+ CVSTag tag = ((TagElement)o).getTag();
+ tagsToRemove.add(tag);
+ }
+ }
+ }
+ if (!tagsToRemove.isEmpty()) {
+ wrappedTagSource.remove((CVSTag[]) tagsToRemove.toArray(new CVSTag[tagsToRemove.size()]));
+ cvsDefinedTagsTree.refresh();
+ cvsDefinedTagsTree.getTree().setFocus();
+ }
+ }
+ private void addDateTagsSelected(CVSTag tag){
+ if(tag == null) return;
+ List knownTags = new ArrayList();
+ knownTags.addAll(Arrays.asList(wrappedTagSource.getTags(CVSTag.DATE)));
+ if(!knownTags.contains( tag)){
+ wrappedTagSource.add(new CVSTag[] { tag });
+ cvsDefinedTagsTree.refresh();
+ cvsDefinedTagsTree.getTree().setFocus();
+ }
+ }
+ private boolean isTagSelectedInKnownTagTree() {
+ IStructuredSelection selection = (IStructuredSelection)cvsDefinedTagsTree.getSelection();
+ if (!selection.isEmpty()) {
+ Iterator it = selection.iterator();
+ while(it.hasNext()) {
+ Object o = it.next();
+ if(o instanceof TagElement) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void removeAllKnownTags() {
+ wrappedTagSource.removeAll();
+ cvsDefinedTagsTree.refresh();
+ }
+
+ private void updateEnablements() {
+ // add checked tags
+ Object[] checked = cvsTagTree.getCheckedElements();
+ addSelectedTagsButton.setEnabled(checked.length!=0?true:false);
+
+ // Remove known tags
+ removeTagButton.setEnabled(isTagSelectedInKnownTagTree()?true:false);
+
+ if(allowSettingAutoRefreshFiles) {
+ // add selected files
+ addSelectedFilesButton.setEnabled(getSelectedFiles().length!=0?true:false);
+
+ // remove auto refresh files
+ removeFileButton.setEnabled(autoRefreshFileList.getSelection().length!=0?true:false);
+ }
+ }
+
+ /**
+ * @see Dialog#okPressed()
+ */
+ protected void okPressed() {
+ try {
+ // save auto refresh file names
+ if(allowSettingAutoRefreshFiles) {
+ RepositoryManager manager = CVSUIPlugin.getPlugin().getRepositoryManager();
+ manager.setAutoRefreshFiles(getSingleFolder(tagSource, false), autoRefreshFileList.getItems());
+ }
+
+ wrappedTagSource.commit(null);
+
+ super.okPressed();
+ } catch (CVSException e) {
+ CVSUIPlugin.openError(getShell(), null, null, e);
+ }
+ }
+
+ protected ICVSFolder getSingleFolder(TagSource tagSource, boolean bestEffort) {
+ if (!bestEffort && tagSource instanceof MultiFolderTagSource)
+ return null;
+ if (tagSource instanceof SingleFolderTagSource)
+ return ((SingleFolderTagSource)tagSource).getFolder();
+ return null;
+ }
+
+ /**
+ * @see Window#getInitialSize()
+ */
+ protected Point getInitialSize() {
+ int width, height;
+ if(allowSettingAutoRefreshFiles) {
+ try {
+ height = settings.getInt(ALLOWREFRESH_HEIGHT_KEY);
+ width = settings.getInt(ALLOWREFRESH_WIDTH_KEY);
+ } catch(NumberFormatException e) {
+ return super.getInitialSize();
+ }
+ } else {
+ try {
+ height = settings.getInt(NOREFRESH_HEIGHT_KEY);
+ width = settings.getInt(NOREFRESH_WIDTH_KEY);
+ } catch(NumberFormatException e) {
+ return super.getInitialSize();
+ }
+ }
+ return new Point(width, height);
+ }
+
+ /**
+ * @see Dialog#cancelPressed()
+ */
+ protected void cancelPressed() {
+ super.cancelPressed();
+ }
+
+ private GridData getStandardButtonData(Button button) {
+ GridData data = new GridData();
+ data.heightHint = convertVerticalDLUsToPixels(IDialogConstants.BUTTON_HEIGHT);
+ //don't crop labels with large font
+ //int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
+ //data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
+ return data;
+ }
+
+ /**
+ * @see Window#close()
+ */
+ public boolean close() {
+ Rectangle bounds = getShell().getBounds();
+ if(allowSettingAutoRefreshFiles) {
+ settings.put(ALLOWREFRESH_HEIGHT_KEY, bounds.height);
+ settings.put(ALLOWREFRESH_WIDTH_KEY, bounds.width);
+ } else {
+ settings.put(NOREFRESH_HEIGHT_KEY, bounds.height);
+ settings.put(NOREFRESH_WIDTH_KEY, bounds.width);
+ }
+ return super.close();
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagContentAssistProcessor.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagContentAssistProcessor.java
new file mode 100644
index 000000000..fe158424a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagContentAssistProcessor.java
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.util.*;
+import java.util.List;
+
+import org.eclipse.jface.contentassist.*;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.contentassist.*;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.ui.contentassist.ContentAssistHandler;
+
+/**
+ * A content assist processor for tags for use with Text widgets.
+ */
+public class TagContentAssistProcessor implements ISubjectControlContentAssistProcessor {
+
+ private FilteredTagList tags;
+ private Map images = new HashMap();
+
+ public static void createContentAssistant(Text text, TagSource tagSource, int includeFlags) {
+ final TagContentAssistProcessor tagContentAssistProcessor = new TagContentAssistProcessor(tagSource, includeFlags);
+ text.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ tagContentAssistProcessor.dispose();
+ }
+ });
+ ContentAssistHandler.createHandlerForText(text, createSubjectContentAssistant(tagContentAssistProcessor));
+ }
+
+ private static SubjectControlContentAssistant createSubjectContentAssistant(IContentAssistProcessor processor) {
+ final SubjectControlContentAssistant contentAssistant= new SubjectControlContentAssistant();
+
+ contentAssistant.setContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE);
+
+ //ContentAssistPreference.configure(contentAssistant, JavaPlugin.getDefault().getPreferenceStore());
+
+ contentAssistant.setContextInformationPopupOrientation(IContentAssistant.CONTEXT_INFO_ABOVE);
+ contentAssistant.setInformationControlCreator(new IInformationControlCreator() {
+ public IInformationControl createInformationControl(Shell parent) {
+ return new DefaultInformationControl(parent);
+ }
+ });
+
+ return contentAssistant;
+ }
+
+ public TagContentAssistProcessor(TagSource tagSource, int includeFlags) {
+ tags = new FilteredTagList(tagSource, TagSource.convertIncludeFlaqsToTagTypes(includeFlags));
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.contentassist.IContentAssistSubjectControl, int)
+ */
+ public ICompletionProposal[] computeCompletionProposals(IContentAssistSubjectControl contentAssistSubjectControl, int documentOffset) {
+ Control c = contentAssistSubjectControl.getControl();
+ int docLength = contentAssistSubjectControl.getDocument().getLength();
+ if (c instanceof Text) {
+ Text t = (Text)c;
+ String filter = t.getText();
+ tags.setPattern(filter);
+ CVSTag[] matching = tags.getMatchingTags();
+ if (matching.length > 0) {
+ List proposals = new ArrayList();
+ for (int i = 0; i < matching.length; i++) {
+ CVSTag tag = matching[i];
+ String name = tag.getName();
+ ImageDescriptor desc = TagElement.getImageDescriptor(tag);
+ Image image = null;
+ if (desc != null) {
+ image = (Image)images.get(desc);
+ if (image == null) {
+ image = desc.createImage();
+ images.put(desc, image);
+ }
+ }
+ CompletionProposal proposal = new CompletionProposal(name, 0, docLength, name.length(), image, name, null, null);
+ proposals.add(proposal);
+ }
+ return (ICompletionProposal[]) proposals.toArray(new ICompletionProposal[proposals.size()]);
+ }
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.contentassist.ISubjectControlContentAssistProcessor#computeContextInformation(org.eclipse.jface.contentassist.IContentAssistSubjectControl, int)
+ */
+ public IContextInformation[] computeContextInformation(IContentAssistSubjectControl contentAssistSubjectControl, int documentOffset) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int)
+ */
+ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
+ Assert.isTrue(false, "ITextViewer not supported"); //$NON-NLS-1$
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int)
+ */
+ public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters()
+ */
+ public char[] getCompletionProposalAutoActivationCharacters() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters()
+ */
+ public char[] getContextInformationAutoActivationCharacters() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage()
+ */
+ public String getErrorMessage() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator()
+ */
+ public IContextInformationValidator getContextInformationValidator() {
+ return null;
+ }
+
+ /**
+ * Dispose of any images created by the assistant
+ */
+ public void dispose() {
+ for (Iterator iter = images.values().iterator(); iter.hasNext();) {
+ Image image = (Image) iter.next();
+ image.dispose();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagElement.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagElement.java
new file mode 100644
index 000000000..139144450
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagElement.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.util.Date;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants;
+import org.eclipse.team.internal.ccvs.ui.model.CVSTagElement;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+public class TagElement implements IWorkbenchAdapter, IAdaptable {
+ Object parent;
+ CVSTag tag;
+
+ public static ImageDescriptor getImageDescriptor(CVSTag tag) {
+ if (tag.getType() == CVSTag.BRANCH || tag.equals(CVSTag.DEFAULT)) {
+ return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_TAG);
+ } else if (tag.getType() == CVSTag.DATE){
+ return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_DATE);
+ }else {
+ return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_PROJECT_VERSION);
+ }
+ }
+
+ /**
+ * @deprecated
+ * @param tag
+ */
+ public TagElement(CVSTag tag) {
+ this(null, tag);
+ }
+ public TagElement(Object parent, CVSTag tag) {
+ this.parent = parent;
+ this.tag = tag;
+ }
+ public Object[] getChildren(Object o) {
+ return new Object[0];
+ }
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class) return this;
+ return null;
+ }
+ public ImageDescriptor getImageDescriptor(Object object) {
+ return getImageDescriptor(tag);
+ }
+ public String getLabel(Object o) {
+ if(tag.getType() == CVSTag.DATE){
+ Date date = tag.asDate();
+ if (date != null){
+ return CVSTagElement.toDisplayString(date);
+ }
+ }
+ return tag.getName();
+ }
+ public Object getParent(Object o) {
+ return parent;
+ }
+ public CVSTag getTag() {
+ return tag;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode() {
+ return tag.hashCode();
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj) {
+ if (obj instanceof TagElement) {
+ return tag.equals(((TagElement)obj).getTag());
+ }
+ return super.equals(obj);
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRefreshButtonArea.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRefreshButtonArea.java
new file mode 100644
index 000000000..a4506ed9f
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRefreshButtonArea.java
@@ -0,0 +1,175 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.core.ICVSFolder;
+import org.eclipse.team.internal.ccvs.core.util.Assert;
+import org.eclipse.team.internal.ccvs.ui.*;
+import org.eclipse.team.internal.ui.dialogs.DialogArea;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.help.WorkbenchHelp;
+
+/**
+ * An area that displays the Refresh and Configure Tags buttons
+ */
+public class TagRefreshButtonArea extends DialogArea {
+
+ private TagSource tagSource;
+ private final Shell shell;
+ private Button refreshButton;
+ private IRunnableContext context;
+
+ public TagRefreshButtonArea(Shell shell, TagSource tagSource) {
+ Assert.isNotNull(shell);
+ Assert.isNotNull(tagSource);
+ this.shell = shell;
+ this.tagSource = tagSource;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ui.dialogs.DialogArea#createArea(org.eclipse.swt.widgets.Composite)
+ */
+ public void createArea(Composite parent) {
+ Composite buttonComp = new Composite(parent, SWT.NONE);
+ GridData data = new GridData ();
+ data.horizontalAlignment = GridData.END;
+ buttonComp.setLayoutData(data);
+ GridLayout layout = new GridLayout();
+ layout.numColumns = 2;
+ layout.marginHeight = 0;
+ layout.marginWidth = 0;
+ buttonComp.setLayout (layout);
+
+ refreshButton = createTagRefreshButton(buttonComp, Policy.bind("TagConfigurationDialog.20")); //$NON-NLS-1$
+ data = new GridData();
+ data.horizontalAlignment = GridData.END;
+ data.horizontalSpan = 1;
+ refreshButton.setLayoutData (data);
+
+ Button addButton = new Button(buttonComp, SWT.PUSH);
+ addButton.setText (Policy.bind("TagConfigurationDialog.21")); //$NON-NLS-1$
+ data = new GridData ();
+ data.horizontalAlignment = GridData.END;
+ data.horizontalSpan = 1;
+ addButton.setLayoutData (data);
+ addButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ TagConfigurationDialog d = new TagConfigurationDialog(shell, tagSource);
+ d.open();
+ }
+ });
+
+ WorkbenchHelp.setHelp(refreshButton, IHelpContextIds.TAG_CONFIGURATION_REFRESHACTION);
+ WorkbenchHelp.setHelp(addButton, IHelpContextIds.TAG_CONFIGURATION_OVERVIEW);
+ Dialog.applyDialogFont(buttonComp);
+ }
+
+ /*
+ * Returns a button that implements the standard refresh tags operation. The runnable is run immediatly after
+ * the tags are fetched from the server. A client should refresh their widgets that show tags because they
+ * may of changed.
+ */
+ private Button createTagRefreshButton(Composite composite, String title) {
+ Button refreshButton = new Button(composite, SWT.PUSH);
+ refreshButton.setText (title);
+ refreshButton.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ try {
+ getRunnableContext().run(true, true, new IRunnableWithProgress() {
+ public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+ try {
+ monitor.beginTask(Policy.bind("TagRefreshButtonArea.5"), 100); //$NON-NLS-1$
+ CVSTag[] tags = tagSource.refresh(false, Policy.subMonitorFor(monitor, 70));
+ if (tags.length == 0 && promptForBestEffort()) {
+ tagSource.refresh(true, Policy.subMonitorFor(monitor, 30));
+ }
+ } catch (TeamException e) {
+ throw new InvocationTargetException(e);
+ } finally {
+ monitor.done();
+ }
+ }
+ });
+ } catch (InterruptedException e) {
+ // operation cancelled
+ } catch (InvocationTargetException e) {
+ CVSUIPlugin.openError(shell, Policy.bind("TagConfigurationDialog.14"), null, e); //$NON-NLS-1$
+ }
+ }
+ });
+ return refreshButton;
+ }
+
+ private boolean promptForBestEffort() {
+ final boolean[] prompt = new boolean[] { false };
+ shell.getDisplay().syncExec(new Runnable() {
+ public void run() {
+ MessageDialog dialog = new MessageDialog(shell, Policy.bind("TagRefreshButtonArea.0"), null, //$NON-NLS-1$
+ getNoTagsFoundMessage(),
+ MessageDialog.INFORMATION,
+ new String[] {
+ Policy.bind("TagRefreshButtonArea.1"), //$NON-NLS-1$
+ Policy.bind("TagRefreshButtonArea.2"), //$NON-NLS-1$
+ Policy.bind("TagRefreshButtonArea.3") //$NON-NLS-1$
+ }, 1);
+ int code = dialog.open();
+ if (code == 0) {
+ prompt[0] = true;
+ } else if (code == 1) {
+ TagConfigurationDialog d = new TagConfigurationDialog(shell, tagSource);
+ d.open();
+ }
+
+ }
+ });
+ return prompt[0];
+ }
+
+ private String getNoTagsFoundMessage() {
+ return Policy.bind("TagRefreshButtonArea.4", tagSource.getShortDescription()); //$NON-NLS-1$
+ }
+
+ protected static ICVSFolder getSingleFolder(TagSource tagSource) {
+ if (tagSource instanceof SingleFolderTagSource)
+ return ((SingleFolderTagSource)tagSource).getFolder();
+ return null;
+ }
+ public TagSource getTagSource() {
+ return tagSource;
+ }
+ public void setTagSource(TagSource tagSource) {
+ Assert.isNotNull(tagSource);
+ this.tagSource = tagSource;
+ }
+
+ public IRunnableContext getRunnableContext() {
+ if (context == null)
+ return PlatformUI.getWorkbench().getProgressService();
+ return context;
+ }
+
+ public void setRunnableContext(IRunnableContext context) {
+ this.context = context;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRootElement.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRootElement.java
new file mode 100644
index 000000000..c9464dde9
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagRootElement.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.ui.*;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+/**
+ * Workbench model element that contains a list of tags
+ * of the same type (BRANCH, VERSION or DATE).
+ */
+public class TagRootElement implements IWorkbenchAdapter, IAdaptable {
+ private TagSource tagSource;
+ private int typeOfTagRoot;
+ private final Object parent;
+
+ public TagRootElement(Object parent, TagSource tagSource, int typeOfTagRoot) {
+ this.parent = parent;
+ this.typeOfTagRoot = typeOfTagRoot;
+ this.tagSource = tagSource;
+ }
+
+ public Object[] getChildren(Object o) {
+ CVSTag[] childTags = tagSource.getTags(typeOfTagRoot);
+ TagElement[] result = new TagElement[childTags.length];
+ for (int i = 0; i < childTags.length; i++) {
+ result[i] = new TagElement(this, childTags[i]);
+ }
+ return result;
+ }
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class) return this;
+ return null;
+ }
+ public ImageDescriptor getImageDescriptor(Object object) {
+ if(typeOfTagRoot==CVSTag.BRANCH) {
+ return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_BRANCHES_CATEGORY);
+ } else if(typeOfTagRoot==CVSTag.DATE){
+ return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_DATES_CATEGORY);
+ }else {
+ return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_VERSIONS_CATEGORY);
+ }
+ }
+ public String getLabel(Object o) {
+ if(typeOfTagRoot==CVSTag.BRANCH) {
+ return Policy.bind("MergeWizardEndPage.branches"); //$NON-NLS-1$
+ } else if(typeOfTagRoot==CVSTag.DATE){
+ return Policy.bind("TagRootElement.0"); //$NON-NLS-1$
+ }else {
+ return Policy.bind("VersionsElement.versions"); //$NON-NLS-1$
+ }
+ }
+ public Object getParent(Object o) {
+ return parent;
+ }
+ /**
+ * Gets the typeOfTagRoot.
+ * @return Returns a int
+ */
+ public int getTypeOfTagRoot() {
+ return typeOfTagRoot;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionArea.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionArea.java
new file mode 100644
index 000000000..3f165b816
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionArea.java
@@ -0,0 +1,612 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.util.*;
+import java.util.List;
+
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.operation.IRunnableContext;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.actions.CVSAction;
+import org.eclipse.team.internal.ccvs.ui.repo.NewDateTagAction;
+import org.eclipse.team.internal.ccvs.ui.repo.RepositoryManager;
+import org.eclipse.team.internal.ccvs.ui.tags.TagSourceWorkbenchAdapter.ProjectElementSorter;
+import org.eclipse.team.internal.ui.dialogs.DialogArea;
+import org.eclipse.ui.help.WorkbenchHelp;
+import org.eclipse.ui.model.WorkbenchContentProvider;
+import org.eclipse.ui.model.WorkbenchLabelProvider;
+import org.eclipse.ui.part.PageBook;
+
+/**
+ * A dialog area that displays a list of tags for selection and supports
+ * filtering of the displayed tags.
+ */
+public class TagSelectionArea extends DialogArea {
+
+ /*
+ * Property constant which identifies the selected tag or
+ * null if no tag is selected
+ */
+ public static final String SELECTED_TAG = "selectedTag"; //$NON-NLS-1$
+
+ /*
+ * Property constant which indicates that a tag has been selected in such
+ * a way as to indicate that this is the desired tag (e.g double-click)
+ */
+ public static final String OPEN_SELECTED_TAG = "openSelectedTag"; //$NON-NLS-1$
+
+ /*
+ * Constants used to configure which tags are shown
+ */
+ public static final int INCLUDE_HEAD_TAG = TagSourceWorkbenchAdapter.INCLUDE_HEAD_TAG;
+ public static final int INCLUDE_BASE_TAG = TagSourceWorkbenchAdapter.INCLUDE_BASE_TAG;
+ public static final int INCLUDE_BRANCHES = TagSourceWorkbenchAdapter.INCLUDE_BRANCHES;
+ public static final int INCLUDE_VERSIONS = TagSourceWorkbenchAdapter.INCLUDE_VERSIONS;
+ public static final int INCLUDE_DATES = TagSourceWorkbenchAdapter.INCLUDE_DATES;
+ public static final int INCLUDE_ALL_TAGS = TagSourceWorkbenchAdapter.INCLUDE_ALL_TAGS;
+
+ private String tagAreaLabel;
+ private final int includeFlags;
+ private CVSTag selection;
+ private String helpContext;
+ private Text filterText;
+ private TagSource tagSource;
+ private final Shell shell;
+ private TagRefreshButtonArea tagRefreshArea;
+ private final TagSource.ITagSourceChangeListener listener = new TagSource.ITagSourceChangeListener() {
+ public void tagsChanged(TagSource source) {
+ Shell shell = getShell();
+ if (!shell.isDisposed()) {
+ shell.getDisplay().syncExec(new Runnable() {
+ public void run() {
+ refresh();
+ }
+ });
+ }
+ }
+ };
+ private final DisposeListener disposeListener = new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (tagSource != null)
+ tagSource.removeListener(listener);
+ }
+ };
+
+ private PageBook switcher;
+ private TreeViewer tagTree;
+ private TableViewer tagTable;
+ private boolean treeVisible = true;
+ private boolean includeFilterInputArea = true;
+ private String filterPattern = ""; //$NON-NLS-1$
+
+ private IRunnableContext context;
+
+ public TagSelectionArea(Shell shell, TagSource tagSource, int includeFlags, String helpContext) {
+ this.shell = shell;
+ this.includeFlags = includeFlags;
+ this.helpContext = helpContext;
+ this.tagSource = tagSource;
+ setSelection(null);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.internal.ui.dialogs.DialogArea#createArea(org.eclipse.swt.widgets.Composite)
+ */
+ public void createArea(Composite parent) {
+ // Create a composite for the entire area
+ Composite outer = createComposite(parent, 1, true);
+ initializeDialogUnits(outer);
+ // Add F1 help
+ if (helpContext != null) {
+ WorkbenchHelp.setHelp(outer, helpContext);
+ }
+
+ // Create the tree area and refresh buttons with the possibility to add stuff in between
+ createTagDisplayArea(outer);
+ createCustomArea(outer);
+ createRefreshButtons(outer);
+
+ Dialog.applyDialogFont(parent);
+ updateTagDisplay(true);
+ }
+
+ private void createTagDisplayArea(Composite parent) {
+ Composite inner = createGrabbingComposite(parent, 1);
+ if (isIncludeFilterInputArea()) {
+ createFilterInput(inner);
+ createWrappingLabel(inner, Policy.bind("TagSelectionArea.0"), 1); //$NON-NLS-1$
+ } else {
+ createWrappingLabel(inner, Policy.bind("TagSelectionArea.1", getTagAreaLabel()), 1); //$NON-NLS-1$
+ }
+ switcher = new PageBook(inner, SWT.NONE);
+ GridData gridData = new GridData(GridData.FILL_BOTH);
+ gridData.heightHint = 0;
+ gridData.widthHint = 0;
+ switcher.setLayoutData(gridData);
+ tagTree = createTree(switcher);
+ tagTable = createTable(switcher);
+ }
+
+ private void createFilterInput(Composite inner) {
+ createWrappingLabel(inner, Policy.bind("TagSelectionArea.2", getTagAreaLabel()), 1); //$NON-NLS-1$
+ filterText = createText(inner, 1);
+ filterText.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ setFilter(filterText.getText());
+ }
+ });
+ filterText.addKeyListener(new KeyListener() {
+ public void keyPressed(KeyEvent e) {
+ if (e.keyCode == SWT.ARROW_DOWN && e.stateMask == 0) {
+ tagTable.getControl().setFocus();
+ }
+ }
+ public void keyReleased(KeyEvent e) {
+ // Ignore
+ }
+ });
+ }
+
+ /**
+ * Return the label that should be used for the tag area.
+ * It should not have any trailing punctuations as the tag area
+ * may position it differently depending on whether the filter
+ * text input is included in the area.
+ * @return the tag area label
+ */
+ public String getTagAreaLabel() {
+ if (tagAreaLabel == null)
+ tagAreaLabel = Policy.bind("TagSelectionArea.3"); //$NON-NLS-1$
+ return tagAreaLabel;
+ }
+
+ /**
+ * Set the label that should be used for the tag area.
+ * It should not have any trailing punctuations as the tag area
+ * may position it differently depending on whether the filter
+ * text input is included in the area.
+ * @param tagAreaLabel the tag area label
+ */
+ public void setTagAreaLabel(String tagAreaLabel) {
+ this.tagAreaLabel = tagAreaLabel;
+ }
+
+ /**
+ * Update the tag display to show the tags that match the
+ * include flags and the filter entered by the user.
+ */
+ protected void updateTagDisplay(boolean firstTime) {
+ String filter = getFilterString();
+ if ((filter != null && filter.length() > 0) || isTableOnly()) {
+ // Show the table and filter it accordingly
+ try {
+ switcher.setRedraw(false);
+ treeVisible = false;
+ switcher.showPage(tagTable.getControl());
+ FilteredTagList list = (FilteredTagList)tagTable.getInput();
+ list.setPattern(filter);
+ tagTable.refresh();
+ if (filterText == null || filter == null || filter.length() == 0) {
+ setSelection(selection);
+ } else {
+ // Only set the top selection if there is a filter from the filter text
+ // of this area. This is done to avoid selection loops
+ selectTopElement();
+ }
+ } finally {
+ switcher.setRedraw(true);
+ }
+ } else {
+ // Show the tree
+ if (!isTreeVisible() || firstTime) {
+ try {
+ switcher.setRedraw(false);
+ treeVisible = true;
+ switcher.showPage(tagTree.getControl());
+ tagTree.refresh();
+ setSelection(selection);
+ } finally {
+ switcher.setRedraw(true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Return whether only a table should be used
+ * @return whether only a table should be used
+ */
+ protected boolean isTableOnly() {
+ return (includeFlags == INCLUDE_VERSIONS) || (includeFlags == INCLUDE_BRANCHES);
+ }
+
+ private String getFilterString() {
+ return filterPattern;
+ }
+
+ /*
+ * Select the top element in the tag table
+ */
+ private void selectTopElement() {
+ if (tagTable.getTable().getItemCount() > 1) {
+ TableItem item = tagTable.getTable().getItem(0);
+ tagTable.getTable().setSelection(new TableItem[] { item });
+ tagTable.setSelection(tagTable.getSelection());
+ }
+ }
+
+ private FilteredTagList createFilteredInput() {
+ return new FilteredTagList(tagSource, TagSource.convertIncludeFlaqsToTagTypes(includeFlags));
+ }
+
+ private Text createText(Composite parent, int horizontalSpan) {
+ Text text = new Text(parent, SWT.BORDER);
+ GridData data = new GridData();
+ data.horizontalSpan = horizontalSpan;
+ data.horizontalAlignment = GridData.FILL;
+ data.grabExcessHorizontalSpace = true;
+ data.widthHint= 0;
+ text.setLayoutData(data);
+ return text;
+ }
+
+ protected void createRefreshButtons(Composite parent) {
+ tagSource.addListener(listener);
+ parent.addDisposeListener(disposeListener);
+ tagRefreshArea = new TagRefreshButtonArea(shell, tagSource);
+ if (context != null)
+ tagRefreshArea.setRunnableContext(context);
+ tagRefreshArea.createArea(parent);
+ }
+
+ protected void createTreeMenu(TreeViewer tagTree) {
+ if ((includeFlags & TagSourceWorkbenchAdapter.INCLUDE_DATES) != 0) {
+ // Create the popup menu
+ MenuManager menuMgr = new MenuManager();
+ Tree tree = tagTree.getTree();
+ Menu menu = menuMgr.createContextMenu(tree);
+ menuMgr.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager manager) {
+ addMenuItemActions(manager);
+ }
+
+ });
+ menuMgr.setRemoveAllWhenShown(true);
+ tree.setMenu(menu);
+ }
+ }
+
+ /**
+ * Create aq custom area that is below the tag selection area but above the refresh busson group
+ * @param parent
+ */
+ protected void createCustomArea(Composite parent) {
+ // No default custom area
+ }
+
+ protected TreeViewer createTree(Composite parent) {
+ Tree tree = new Tree(parent, SWT.MULTI | SWT.BORDER);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ tree.setLayoutData(data);
+ TreeViewer result = new TreeViewer(tree);
+ initialize(result);
+ result.getControl().addKeyListener(new KeyListener() {
+ public void keyPressed(KeyEvent event) {
+ handleKeyPressed(event);
+ }
+ public void keyReleased(KeyEvent event) {
+ handleKeyReleased(event);
+ }
+ });
+ result.setInput(createUnfilteredInput());
+ createTreeMenu(result);
+ return result;
+ }
+
+ protected TableViewer createTable(Composite parent) {
+ Table table = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.SINGLE | SWT.FULL_SELECTION);
+ GridData data = new GridData(GridData.FILL_BOTH);
+ table.setLayoutData(data);
+ TableLayout layout = new TableLayout();
+ layout.addColumnData(new ColumnWeightData(100, true));
+ table.setLayout(layout);
+ TableColumn col = new TableColumn(table, SWT.NONE);
+ col.setResizable(true);
+ TableViewer viewer = new TableViewer(table);
+ initialize(viewer);
+ viewer.setInput(createFilteredInput());
+ return viewer;
+
+ }
+
+ private void initialize(StructuredViewer viewer) {
+ viewer.setContentProvider(new WorkbenchContentProvider());
+ viewer.setLabelProvider(new WorkbenchLabelProvider());
+ viewer.setSorter(new ProjectElementSorter());
+ viewer.addSelectionChangedListener(new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ handleSelectionChange();
+ }
+ });
+ // select and close on double click
+ // To do: use defaultselection instead of double click
+ viewer.getControl().addMouseListener(new MouseAdapter() {
+ public void mouseDoubleClick(MouseEvent e) {
+ CVSTag tag = internalGetSelectedTag();
+ if (tag != null) {
+ firePropertyChangeChange(OPEN_SELECTED_TAG, null, tag);
+ }
+ }
+ });
+ }
+
+ private Object createUnfilteredInput() {
+ return TagSourceWorkbenchAdapter.createInput(tagSource, includeFlags);
+ }
+
+ public void handleKeyPressed(KeyEvent event) {
+ if (event.character == SWT.DEL && event.stateMask == 0) {
+ deleteDateTag();
+ }
+ }
+ private void deleteDateTag() {
+ TagElement[] selectedDateTagElements = getSelectedDateTagElement();
+ if (selectedDateTagElements.length == 0) return;
+ for(int i = 0; i < selectedDateTagElements.length; i++){
+ RepositoryManager mgr = CVSUIPlugin.getPlugin().getRepositoryManager();
+ CVSTag tag = selectedDateTagElements[i].getTag();
+ if(tag.getType() == CVSTag.DATE){
+ mgr.removeDateTag(getLocation(),tag);
+ }
+ }
+ tagTree.refresh();
+ handleSelectionChange();
+ }
+
+ /**
+ * Returns the selected date tag elements
+ */
+ private TagElement[] getSelectedDateTagElement() {
+ ArrayList dateTagElements = null;
+ IStructuredSelection selection = (IStructuredSelection)tagTree.getSelection();
+ if (selection!=null && !selection.isEmpty()) {
+ dateTagElements = new ArrayList();
+ Iterator elements = selection.iterator();
+ while (elements.hasNext()) {
+ Object next = CVSAction.getAdapter(elements.next(), TagElement.class);
+ if (next instanceof TagElement) {
+ if(((TagElement)next).getTag().getType() == CVSTag.DATE){
+ dateTagElements.add(next);
+ }
+ }
+ }
+ }
+ if (dateTagElements != null && !dateTagElements.isEmpty()) {
+ TagElement[] result = new TagElement[dateTagElements.size()];
+ dateTagElements.toArray(result);
+ return result;
+ }
+ return new TagElement[0];
+ }
+ private void addDateTag(CVSTag tag){
+ if(tag == null) return;
+ List dateTags = new ArrayList();
+ ICVSRepositoryLocation location = getLocation();
+ dateTags.addAll(Arrays.asList(CVSUIPlugin.getPlugin().getRepositoryManager().getDateTags(location)));
+ if(!dateTags.contains( tag)){
+ CVSUIPlugin.getPlugin().getRepositoryManager().addDateTag(location, tag);
+ }
+ try {
+ tagTree.getControl().setRedraw(false);
+ tagTree.refresh();
+ setSelection(tag);
+ } finally {
+ tagTree.getControl().setRedraw(true);
+ }
+ handleSelectionChange();
+ }
+ private void addMenuItemActions(IMenuManager manager) {
+ manager.add(new Action(Policy.bind("TagSelectionDialog.0")) { //$NON-NLS-1$
+ public void run() {
+ CVSTag dateTag = NewDateTagAction.getDateTag(getShell(), getLocation());
+ addDateTag(dateTag);
+ }
+ });
+ if(getSelectedDateTagElement().length > 0){
+ manager.add(new Action(Policy.bind("TagSelectionDialog.1")) { //$NON-NLS-1$
+ public void run() {
+ deleteDateTag();
+ }
+ });
+ }
+ }
+
+ protected void handleKeyReleased(KeyEvent event) {
+ }
+
+ /**
+ * handle a selection change event from the visible tag display
+ * (which could be either the table or the tree).
+ */
+ protected void handleSelectionChange() {
+ CVSTag newSelection = internalGetSelectedTag();
+ if (selection != null && newSelection != null && selection.equals(newSelection)) {
+ // the selection hasn't change so return
+ return;
+ }
+ CVSTag oldSelection = selection;
+ selection = newSelection;
+ firePropertyChangeChange(SELECTED_TAG, oldSelection, selection);
+ }
+
+ private CVSTag internalGetSelectedTag() {
+ IStructuredSelection selection;
+ if (isTreeVisible()) {
+ selection = (IStructuredSelection)tagTree.getSelection();
+ } else {
+ selection = (IStructuredSelection)tagTable.getSelection();
+ }
+ Object o = selection.getFirstElement();
+ if (o instanceof TagElement)
+ return ((TagElement)o).getTag();
+ return null;
+ }
+
+ private boolean isTreeVisible() {
+ return treeVisible;
+ }
+
+ private ICVSRepositoryLocation getLocation(){
+ return tagSource.getLocation();
+ }
+ public CVSTag getSelection() {
+ return selection;
+ }
+ public Shell getShell() {
+ return shell;
+ }
+
+ /**
+ * Set the focus to the filter text widget
+ */
+ public void setFocus() {
+ if (filterText != null)
+ filterText.setFocus();
+ else if (switcher != null)
+ switcher.setFocus();
+
+ // Refresh in case tags were added since the last time the area had focus
+ refresh();
+ }
+
+ /**
+ * Select the given tag
+ * @param selectedTag the tag to be selected
+ */
+ public void setSelection(CVSTag selectedTag) {
+ if (isTreeVisible())
+ if (tagTree != null && !tagTree.getControl().isDisposed()) {
+ // TODO: Hack to instantiate the model before revealing the selection
+ tagTree.expandToLevel(2);
+ tagTree.collapseAll();
+ // Reveal the selection
+ tagTree.reveal(new TagElement(selectedTag));
+ tagTree.setSelection(new StructuredSelection(new TagElement(selectedTag)));
+ }
+ else
+ if (tagTable != null && !tagTable.getControl().isDisposed()) {
+ tagTable.setSelection(new StructuredSelection(new TagElement(selectedTag)));
+ }
+ }
+
+ /**
+ * Refresh the state of the tag selection area
+ */
+ public void refresh() {
+ if (isTreeVisible()) {
+ if (tagTree != null && !tagTree.getControl().isDisposed()) {
+ tagTree.refresh();
+ }
+ } else {
+ if (tagTable != null && !tagTable.getControl().isDisposed()) {
+ tagTable.refresh();
+ }
+ }
+ }
+
+ /**
+ * Set the enablement state of the area
+ * @param enabled the enablement state
+ */
+ public void setEnabled(boolean enabled) {
+ if (filterText != null)
+ filterText.setEnabled(enabled);
+ tagTree.getControl().setEnabled(enabled);
+ tagTable.getControl().setEnabled(enabled);
+ }
+
+ /**
+ * Get the tag source for the tags being displayed
+ * @return the tag source for the tags being displayed
+ */
+ public TagSource getTagSource() {
+ return tagSource;
+ }
+
+ /**
+ * Set the tag source from which the displayed tags are determined
+ * @param tagSource the source of the tags being displayed
+ */
+ public void setTagSource(TagSource tagSource) {
+ if (this.tagSource != null) {
+ this.tagSource.removeListener(listener);
+ }
+ this.tagSource = tagSource;
+ this.tagSource.addListener(listener);
+ tagRefreshArea.setTagSource(this.tagSource);
+ setTreeAndTableInput();
+ }
+
+ private void setTreeAndTableInput() {
+ if (tagTree != null) {
+ tagTree.setInput(createUnfilteredInput());
+ }
+ if (tagTable != null) {
+ tagTable.setInput(createFilteredInput());
+ }
+
+ }
+
+ /**
+ * Set whether the input filter text is to be included in the tag selection area.
+ * If excluded, clientscan still set the filter text directly using
+ * <code>setFilter</code>.
+ * @param include whether filter text input should be included
+ */
+ public void setIncludeFilterInputArea(boolean include) {
+ includeFilterInputArea = include;
+ }
+
+ /**
+ * Return whether the input filter text is to be included in the tag selection area.
+ * If excluded, clientscan still set the filter text directly using
+ * <code>setFilter</code>.
+ * @return whether filter text input should be included
+ */
+ public boolean isIncludeFilterInputArea() {
+ return includeFilterInputArea;
+ }
+
+ /**
+ * Set the text used to filter the tag list.
+ * @param filter the filter pattern
+ */
+ public void setFilter(String filter) {
+ this.filterPattern = filter;
+ updateTagDisplay(false);
+ }
+
+ public void setRunnableContext(IRunnableContext context) {
+ this.context = context;
+ if (tagRefreshArea != null)
+ tagRefreshArea.setRunnableContext(context);
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionDialog.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionDialog.java
new file mode 100644
index 000000000..30050625d
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionDialog.java
@@ -0,0 +1,247 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.ui.IHelpContextIds;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+
+/**
+ * Dialog to prompt the user to choose a tag for a selected resource
+ */
+public class TagSelectionDialog extends Dialog implements IPropertyChangeListener {
+
+ private TagSelectionArea tagSelectionArea;
+
+ public static final int INCLUDE_HEAD_TAG = TagSourceWorkbenchAdapter.INCLUDE_HEAD_TAG;
+ public static final int INCLUDE_BASE_TAG = TagSourceWorkbenchAdapter.INCLUDE_BASE_TAG;
+ public static final int INCLUDE_BRANCHES = TagSourceWorkbenchAdapter.INCLUDE_BRANCHES;
+ public static final int INCLUDE_VERSIONS = TagSourceWorkbenchAdapter.INCLUDE_VERSIONS;
+ public static final int INCLUDE_DATES = TagSourceWorkbenchAdapter.INCLUDE_DATES;
+ public static final int INCLUDE_ALL_TAGS = TagSourceWorkbenchAdapter.INCLUDE_ALL_TAGS;
+
+ private Button okButton;
+
+ // dialog title, should indicate the action in which the tag selection
+ // dialog is being shown
+ private String title;
+
+ private boolean recurse = true;
+
+ // constants
+ private static final int SIZING_DIALOG_WIDTH = 400;
+ private static final int SIZING_DIALOG_HEIGHT = 400;
+
+ private CVSTag selection;
+
+ private TagSource tagSource;
+
+ private String message;
+
+ private int includeFlags;
+
+ private String helpContext;
+
+ private boolean showRecurse;
+
+ public static CVSTag getTagToCompareWith(Shell shell, TagSource tagSource, int includeFlags) {
+ TagSelectionDialog dialog = new TagSelectionDialog(shell, tagSource,
+ Policy.bind("CompareWithTagAction.message"), //$NON-NLS-1$
+ Policy.bind("TagSelectionDialog.Select_a_Tag_1"), //$NON-NLS-1$
+ includeFlags,
+ false, /* show recurse*/
+ IHelpContextIds.COMPARE_TAG_SELECTION_DIALOG);
+ dialog.setBlockOnOpen(true);
+ int result = dialog.open();
+ if (result == Dialog.CANCEL) {
+ return null;
+ }
+ return dialog.getResult();
+ }
+
+ /**
+ * Creates a new TagSelectionDialog.
+ * @param resource The resource to select a version for.
+ */
+ public TagSelectionDialog(Shell parentShell, TagSource tagSource, String title, String message, int includeFlags, final boolean showRecurse, String helpContext) {
+ super(parentShell);
+
+ // Create a tag selection area with a custom recurse option
+ this.tagSource = tagSource;
+ this.message = message;
+ this.includeFlags = includeFlags;
+ this.helpContext = helpContext;
+ this.showRecurse = showRecurse;
+ this.title = title;
+ setShellStyle(getShellStyle() | SWT.RESIZE);
+ }
+
+ /* (non-Javadoc)
+ * Method declared on Window.
+ */
+ protected void configureShell(Shell newShell) {
+ super.configureShell(newShell);
+ newShell.setText(title);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.window.Window#getInitialSize()
+ */
+ protected Point getInitialSize() {
+ return new Point(SIZING_DIALOG_WIDTH, SIZING_DIALOG_HEIGHT);
+ }
+
+ /**
+ * Creates this window's widgetry.
+ * <p>
+ * The default implementation of this framework method
+ * creates this window's shell (by calling <code>createShell</code>),
+ * its control (by calling <code>createContents</code>),
+ * and initializes this window's shell bounds
+ * (by calling <code>initializeBounds</code>).
+ * This framework method may be overridden; however,
+ * <code>super.create</code> must be called.
+ * </p>
+ */
+ public void create() {
+ super.create();
+ initialize();
+ }
+
+ /**
+ * Add buttons to the dialog's button bar.
+ *
+ * @param parent the button bar composite
+ */
+ protected void createButtonsForButtonBar(Composite parent) {
+ // create OK and Cancel buttons by default
+ okButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+ okButton.setEnabled(false);
+ createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
+ }
+
+ /**
+ * Creates and returns the contents of the upper part
+ * of this dialog (above the button bar).
+ * <p>
+ * The default implementation of this framework method
+ * creates and returns a new <code>Composite</code> with
+ * standard margins and spacing.
+ * Subclasses should override.
+ * </p>
+ *
+ * @param the parent composite to contain the dialog area
+ * @return the dialog area control
+ */
+ protected Control createDialogArea(Composite parent) {
+ Composite top = (Composite)super.createDialogArea(parent);
+
+ // Delegate most of the dialog to the tag selection area
+ tagSelectionArea = new TagSelectionArea(getShell(), tagSource, includeFlags, helpContext) {
+ protected void createCustomArea(Composite parent) {
+ if(showRecurse) {
+ final Button recurseCheck = new Button(parent, SWT.CHECK);
+ recurseCheck.setText(Policy.bind("TagSelectionDialog.recurseOption")); //$NON-NLS-1$
+ recurseCheck.addListener(SWT.Selection, new Listener() {
+ public void handleEvent(Event event) {
+ recurse = recurseCheck.getSelection();
+ }
+ });
+ recurseCheck.setSelection(true);
+ }
+ }
+ };
+ if (message != null)
+ tagSelectionArea.setTagAreaLabel(message);
+ tagSelectionArea.addPropertyChangeListener(this);
+ tagSelectionArea.createArea(top);
+
+ // Create a separator between the tag area and the button area
+ Label seperator = new Label(top, SWT.SEPARATOR | SWT.HORIZONTAL);
+ GridData data = new GridData (GridData.FILL_HORIZONTAL);
+ data.horizontalSpan = 2;
+ seperator.setLayoutData(data);
+
+ updateEnablement();
+ Dialog.applyDialogFont(parent);
+
+ return top;
+ }
+
+
+ /**
+ * Utility method that creates a label instance
+ * and sets the default layout data.
+ *
+ * @param parent the parent for the new label
+ * @param text the text for the new label
+ * @return the new label
+ */
+ protected Label createLabel(Composite parent, String text) {
+ Label label = new Label(parent, SWT.LEFT);
+ label.setText(text);
+ GridData data = new GridData();
+ data.horizontalSpan = 1;
+ data.horizontalAlignment = GridData.FILL;
+ label.setLayoutData(data);
+ return label;
+ }
+
+ /**
+ * Returns the selected tag.
+ */
+ public CVSTag getResult() {
+ return selection;
+ }
+
+ public boolean getRecursive() {
+ return recurse;
+ }
+
+ /**
+ * Initializes the dialog contents.
+ */
+ protected void initialize() {
+ okButton.setEnabled(false);
+ }
+
+
+ /**
+ * Updates the dialog enablement.
+ */
+ protected void updateEnablement() {
+ if(okButton!=null) {
+ okButton.setEnabled(selection != null);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent event) {
+ String property = event.getProperty();
+ if (property.equals(TagSelectionArea.SELECTED_TAG)) {
+ selection = (CVSTag)event.getNewValue();
+ updateEnablement();
+ } else if (property.equals(TagSelectionArea.OPEN_SELECTED_TAG)) {
+ okPressed();
+ }
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionWizardPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionWizardPage.java
new file mode 100644
index 000000000..f05330b3a
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSelectionWizardPage.java
@@ -0,0 +1,181 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.wizards.CVSWizardPage;
+import org.eclipse.ui.help.WorkbenchHelp;
+
+/**
+ * General tag selection page that allows the selection of a tag
+ * for a particular remote folder
+ */
+public class TagSelectionWizardPage extends CVSWizardPage {
+
+ private CVSTag selectedTag;
+
+ // Needed to dynamicaly create refresh buttons
+ private Composite composite;
+
+ private int includeFlags;
+
+ // Fields for allowing the use of the tag from the local workspace
+ boolean allowNoTag = false;
+ private Button useResourceTagButton;
+ private Button selectTagButton;
+ private boolean useResourceTag = false;
+ private String helpContextId;
+ private TagSelectionArea tagArea;
+ private String tagLabel;
+ private TagSource tagSource;
+
+ public TagSelectionWizardPage(String pageName, String title, ImageDescriptor titleImage, String description, TagSource tagSource, int includeFlags) {
+ super(pageName, title, titleImage, description);
+ this.tagSource = tagSource;
+ this.includeFlags = includeFlags;
+ }
+
+ /**
+ * Set the help context for the tag selection page.
+ * This method must be invoked before <code>createControl</code>
+ * @param helpContextId the help context id
+ */
+ public void setHelpContxtId(String helpContextId) {
+ this.helpContextId = helpContextId;
+ }
+
+ /**
+ * Set the label to appear over the tag list/tree.
+ * If not set, a default will be used.
+ * * This method must be invoked before <code>createControl</code>
+ * @param tagLabel the label to appear over the tag list/tree
+ */
+ public void setTagLabel(String tagLabel) {
+ this.tagLabel = tagLabel;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent) {
+ composite = createComposite(parent, 1, false);
+ setControl(composite);
+
+ // set F1 help
+ if (helpContextId != null)
+ WorkbenchHelp.setHelp(composite, helpContextId);
+
+ if (allowNoTag) {
+ SelectionListener listener = new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ useResourceTag = useResourceTagButton.getSelection();
+ updateEnablement();
+ }
+ };
+ useResourceTag = true;
+ useResourceTagButton = createRadioButton(composite, Policy.bind("TagSelectionWizardPage.0"), 1); //$NON-NLS-1$
+ selectTagButton = createRadioButton(composite, Policy.bind("TagSelectionWizardPage.1"), 1); //$NON-NLS-1$
+ useResourceTagButton.setSelection(useResourceTag);
+ selectTagButton.setSelection(!useResourceTag);
+ useResourceTagButton.addSelectionListener(listener);
+ selectTagButton.addSelectionListener(listener);
+ }
+
+ createTagArea();
+ updateEnablement();
+ Dialog.applyDialogFont(parent);
+ }
+
+ private void createTagArea() {
+ tagArea = new TagSelectionArea(getShell(), tagSource, includeFlags, null);
+ if (tagLabel != null)
+ tagArea.setTagAreaLabel(tagLabel);
+ tagArea.setRunnableContext(getContainer());
+ tagArea.createArea(composite);
+ tagArea.addPropertyChangeListener(new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ if (event.getProperty().equals(TagSelectionArea.SELECTED_TAG)) {
+ selectedTag = tagArea.getSelection();
+ updateEnablement();
+ } else if (event.getProperty().equals(TagSelectionArea.OPEN_SELECTED_TAG)) {
+ if (selectedTag != null)
+ gotoNextPage();
+ }
+
+ }
+ });
+ refreshTagArea();
+ }
+
+ private void refreshTagArea() {
+ if (tagArea != null) {
+ tagArea.refresh();
+ tagArea.setSelection(selectedTag);
+ }
+ }
+
+ protected void updateEnablement() {
+ tagArea.setEnabled(!useResourceTag);
+ setPageComplete(useResourceTag || selectedTag != null);
+ }
+
+ public CVSTag getSelectedTag() {
+ if (useResourceTag)
+ return null;
+ return selectedTag;
+ }
+
+ protected void gotoNextPage() {
+ TagSelectionWizardPage.this.getContainer().showPage(getNextPage());
+ }
+
+ public void setAllowNoTag(boolean b) {
+ allowNoTag = b;
+ }
+
+ public void setVisible(boolean visible) {
+ super.setVisible(visible);
+ if (visible && tagArea != null) {
+ tagArea.setFocus();
+ }
+ }
+
+ /**
+ * Set the tag source used by this wizard page
+ * @param source the tag source
+ */
+ public void setTagSource(TagSource source) {
+ this.tagSource = source;
+ tagArea.setTagSource(tagSource);
+ setSelection(null);
+ refreshTagArea();
+ }
+
+ /**
+ * Set the selection of the page to the given tag
+ * @param selectedTag
+ */
+ public void setSelection(CVSTag selectedTag) {
+ if (selectedTag == null && (includeFlags & TagSelectionArea.INCLUDE_HEAD_TAG) > 0) {
+ this.selectedTag = CVSTag.DEFAULT;
+ } else {
+ this.selectedTag = selectedTag;
+ }
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSource.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSource.java
new file mode 100644
index 000000000..0e9540bfb
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSource.java
@@ -0,0 +1,234 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.util.*;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.util.ListenerList;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
+
+/**
+ * A tag source provides access to a set of tags.
+ */
+public abstract class TagSource {
+
+ /*
+ * Special constant representing the BASE tag
+ */
+ public static final int BASE = -1;
+
+ public static final TagSource EMPTY = new TagSource() {
+ public void commit(CVSTag[] tags, boolean replace, IProgressMonitor monitor) throws CVSException {
+ // No-op
+ }
+ public ICVSRepositoryLocation getLocation() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+ public String getShortDescription() {
+ return "Empty"; //$NON-NLS-1$
+ }
+ public CVSTag[] getTags(int type) {
+ return new CVSTag[0];
+ }
+ public CVSTag[] refresh(boolean bestEffort, IProgressMonitor monitor) throws TeamException {
+ return new CVSTag[0];
+ }
+ public ICVSResource[] getCVSResources() {
+ return new ICVSResource[0];
+ }
+ };
+
+ private ListenerList listeners = new ListenerList();
+
+ /**
+ * Simple interface for providing notification when the tags
+ * for this source have changed.
+ */
+ public interface ITagSourceChangeListener {
+ void tagsChanged(TagSource source);
+ }
+
+ public static int[] convertIncludeFlaqsToTagTypes(int includeFlags) {
+ List types = new ArrayList();
+ if ((includeFlags & TagSelectionArea.INCLUDE_BRANCHES) > 0)
+ types.add(new Integer(CVSTag.BRANCH));
+ if ((includeFlags & TagSelectionArea.INCLUDE_VERSIONS) > 0)
+ types.add(new Integer(CVSTag.VERSION));
+ if ((includeFlags & (TagSelectionArea.INCLUDE_HEAD_TAG)) > 0)
+ types.add(new Integer(CVSTag.HEAD));
+ if ((includeFlags & (TagSelectionArea.INCLUDE_DATES)) > 0)
+ types.add(new Integer(CVSTag.DATE));
+ if ((includeFlags & (TagSelectionArea.INCLUDE_BASE_TAG)) > 0)
+ types.add(new Integer(BASE));
+ int[] result = new int[types.size()];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = ((Integer)types.get(i)).intValue();
+
+ }
+ return result;
+ }
+
+ /**
+ * Create a tag source for the given folders
+ * @param folders one or more folders
+ * @return a tag source for the supplied folders
+ */
+ public static TagSource create(ICVSFolder[] folders) {
+ if (folders.length == 1) {
+ return new SingleFolderTagSource(folders[0]);
+ } else {
+ return new MultiFolderTagSource(folders);
+ }
+ }
+
+ /**
+ * Create a tag source for a list of resources
+ * @param resources one or more resources
+ * @return a tag source
+ */
+ public static TagSource create(ICVSResource[] resources) {
+ if (resources.length == 1 && !resources[0].isFolder())
+ return new SingleFileTagSource((ICVSFile)resources[0]);
+ return create(getFolders(resources));
+ }
+
+ private static ICVSFolder[] getFolders(ICVSResource[] resources) {
+ return new ICVSFolder[] { getFirstFolder(resources) } ;
+ }
+
+ /**
+ * Create a tag source for a list of resources
+ * @param resources one or more resources
+ * @return a tag source
+ */
+ public static TagSource create(IResource[] resources) {
+ return create(getCVSResources(getProjects(resources)));
+ }
+
+ private static IResource[] getProjects(IResource[] resources) {
+ Set result = new HashSet();
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ result.add(resource.getProject());
+ }
+ return (IResource[]) result.toArray(new IResource[result.size()]);
+ }
+
+ /**
+ * Return a tag source for a single remote folder
+ * @param remote the remote folder
+ * @return a tag source for that folder
+ */
+ public static TagSource create(ICVSRemoteFolder remote) {
+ return new SingleFolderTagSource(remote);
+ }
+
+ private static ICVSResource[] getCVSResources(IResource[] resources) {
+ List cvsResources = new ArrayList();
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ cvsResources.add(CVSWorkspaceRoot.getCVSResourceFor(resource));
+ }
+ return (ICVSResource[]) cvsResources.toArray(new ICVSResource[cvsResources.size()]);
+ }
+
+ private static ICVSFolder getFirstFolder(ICVSResource[] resources) {
+ if (resources[0].isFolder()) {
+ return (ICVSFolder)resources[0];
+ } else {
+ return resources[0].getParent();
+ }
+ }
+
+ public CVSTag[] getTags(int type) {
+ switch (type) {
+ case BASE:
+ return new CVSTag[] { CVSTag.BASE };
+ case CVSTag.HEAD:
+ return new CVSTag[] { CVSTag.DEFAULT };
+ }
+ return new CVSTag[0];
+ }
+
+ public CVSTag[] getTags(int[] types) {
+ if (types.length == 0) {
+ return new CVSTag[0];
+ }
+ if (types.length == 1) {
+ return getTags(types[0]);
+ }
+ List result = new ArrayList();
+ for (int i = 0; i < types.length; i++) {
+ int type = types[i];
+ CVSTag[] tags = getTags(type);
+ result.addAll(Arrays.asList(tags));
+ }
+ return (CVSTag[]) result.toArray(new CVSTag[result.size()]);
+ }
+
+ /**
+ * Refresh the tags by contacting the server if appropriate
+ * @param monitor a progress monitor
+ * @param bestEffort if best effort is true, then the whole folder contents may be searched
+ * @return any discovered tags
+ */
+ public abstract CVSTag[] refresh(boolean bestEffort, IProgressMonitor monitor) throws TeamException;
+
+ public abstract ICVSRepositoryLocation getLocation();
+
+ /**
+ * Return a short description of the tag source for displaying in UI.
+ * @return a short description of the tag source for displaying in UI.
+ */
+ public abstract String getShortDescription();
+
+ /**
+ * Commit a set of tag changes to the tag cache
+ * @param tags the tags that should be cached
+ * @param replace whether existing tags not in the list should be removed
+ * @param monitor a progress monitor
+ * @throws CVSException
+ */
+ public abstract void commit(CVSTag[] tags, boolean replace, IProgressMonitor monitor) throws CVSException;
+
+ public void addListener(ITagSourceChangeListener listener) {
+ listeners.add(listener);
+ }
+
+ public void removeListener(ITagSourceChangeListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Notify all listeners that the tags from this source may have changed
+ */
+ public void fireChange() {
+ Object[] list = listeners.getListeners();
+ for (int i = 0; i < list.length; i++) {
+ final ITagSourceChangeListener listener = (ITagSourceChangeListener)list[i];
+ Platform.run(new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ // logged by run
+ }
+ public void run() throws Exception {
+ listener.tagsChanged(TagSource.this);
+ }
+ });
+ }
+ }
+
+ public abstract ICVSResource[] getCVSResources();
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceResourceAdapter.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceResourceAdapter.java
new file mode 100644
index 000000000..d3902327f
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceResourceAdapter.java
@@ -0,0 +1,88 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+/**
+ * A workbench adapter that can be used to view the resources that make up
+ * a tag source. It is used by the TagConfigurationDialog.
+ */
+public class TagSourceResourceAdapter implements IAdaptable, IWorkbenchAdapter {
+
+ public static Object getViewerInput(TagSource tagSource) {
+ return new TagSourceResourceAdapter(tagSource);
+ }
+
+ TagSource tagSource;
+
+ private TagSourceResourceAdapter(TagSource tagSource) {
+ this.tagSource = tagSource;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)
+ */
+ public Object[] getChildren(Object o) {
+ ICVSResource[] children = tagSource.getCVSResources();
+ if (children.length == 0) return new Object[0];
+ List result = new ArrayList();
+ for (int i = 0; i < children.length; i++) {
+ ICVSResource resource = children[i];
+ if (resource.isFolder()) {
+ result.add(new CVSFolderElement((ICVSFolder)resource, false));
+ } else {
+ result.add(new CVSFileElement((ICVSFile)resource));
+ }
+ }
+ return result.toArray(new Object[result.size()]);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)
+ */
+ public ImageDescriptor getImageDescriptor(Object object) {
+ // No imgae descriptor
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)
+ */
+ public String getLabel(Object o) {
+ return tagSource.getShortDescription();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)
+ */
+ public Object getParent(Object o) {
+ // No parent
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class) {
+ return this;
+ }
+ return null;
+ }
+
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceWorkbenchAdapter.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceWorkbenchAdapter.java
new file mode 100644
index 000000000..24de6170e
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/tags/TagSourceWorkbenchAdapter.java
@@ -0,0 +1,157 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.tags;
+
+import java.util.ArrayList;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerSorter;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+/**
+ * A workbench adapter for a tag source that creates a model
+ * for displaying the tags from a tag source in a tree or table
+ * viewer. The workbench adapter is not a singleton since it needs
+ * to be configured to display certain types of tags.
+ */
+public class TagSourceWorkbenchAdapter implements IAdaptable, IWorkbenchAdapter {
+
+ /**
+ * Constants for configuring which types of tags should be displayed.
+ */
+ public static final int INCLUDE_HEAD_TAG = 1;
+ public static final int INCLUDE_BASE_TAG = 2;
+ public static final int INCLUDE_BRANCHES = 4;
+ public static final int INCLUDE_VERSIONS = 8;
+ public static final int INCLUDE_DATES = 16;
+ public static final int INCLUDE_ALL_TAGS = INCLUDE_HEAD_TAG | INCLUDE_BASE_TAG | INCLUDE_BRANCHES | INCLUDE_VERSIONS | INCLUDE_DATES;
+
+ TagRootElement branches;
+ TagRootElement versions;
+ TagRootElement dates;
+ int includeFlags;
+
+ public static class ProjectElementSorter extends ViewerSorter {
+
+ /*
+ * The order in the diaog should be HEAD, Branches, Versions, Dates, BASE
+ */
+ public int category(Object element) {
+ if (element instanceof TagElement) {
+ CVSTag tag = ((TagElement)element).getTag();
+ if (tag == CVSTag.DEFAULT) return 1;
+ if (tag == CVSTag.BASE) return 5;
+ if (tag.getType() == CVSTag.BRANCH) return 2;
+ if (tag.getType() == CVSTag.VERSION) return 3;
+ if (tag.getType() == CVSTag.DATE) return 4;
+ } else if (element instanceof TagRootElement) {
+ if(((TagRootElement)element).getTypeOfTagRoot() == CVSTag.BRANCH) return 2;
+ if(((TagRootElement)element).getTypeOfTagRoot() == CVSTag.VERSION) return 3;
+ if(((TagRootElement)element).getTypeOfTagRoot() == CVSTag.DATE) return 4;
+ }
+ return 0;
+ }
+ public int compare(Viewer viewer, Object e1, Object e2) {
+ int cat1 = category(e1);
+ int cat2 = category(e2);
+ if (cat1 != cat2) return cat1 - cat2;
+ // Sort version tags in reverse order
+ if (e1 instanceof TagElement){
+ CVSTag tag1 = ((TagElement)e1).getTag();
+ int type = tag1.getType();
+ if(type == CVSTag.VERSION) {
+ return -1 * super.compare(viewer, e1, e2);
+ }else if(type == CVSTag.DATE){
+ return -1*tag1.compareTo(((TagElement)e2).getTag());
+ }
+ }
+ return super.compare(viewer, e1, e2);
+ }
+ }
+
+
+ /**
+ * Create a viewer input for the tag source
+ * @param tagSource the tag source
+ * @param includeFlags the types of tags to include
+ * @return a tree viewer input
+ */
+ public static Object createInput(TagSource tagSource, int includeFlags) {
+ if (includeFlags == INCLUDE_VERSIONS) {
+ // Versions only is requested by the merge start page.
+ // Only need to show version tags
+ return new TagRootElement(null, tagSource, CVSTag.VERSION);
+ }
+ return new TagSourceWorkbenchAdapter(tagSource, includeFlags);
+ }
+
+ public TagSourceWorkbenchAdapter(TagSource tagSource, int includeFlags) {
+ this.includeFlags = includeFlags;
+ if (this.includeFlags == 0) this.includeFlags = INCLUDE_ALL_TAGS;
+ if ((includeFlags & INCLUDE_BRANCHES) > 0) {
+ branches = new TagRootElement(this, tagSource, CVSTag.BRANCH);
+ }
+ if ((includeFlags & INCLUDE_VERSIONS) > 0) {
+ versions = new TagRootElement(this, tagSource, CVSTag.VERSION);
+ }
+ if ((includeFlags & INCLUDE_DATES) > 0) {
+ dates = new TagRootElement(this, tagSource, CVSTag.DATE);
+ }
+ }
+
+ public Object[] getChildren(Object o) {
+ ArrayList children = new ArrayList(4);
+ if ((includeFlags & INCLUDE_HEAD_TAG) > 0) {
+ children.add(new TagElement(this, CVSTag.DEFAULT));
+ }
+ if ((includeFlags & INCLUDE_BASE_TAG) > 0) {
+ children.add(new TagElement(this, CVSTag.BASE));
+ }
+ if ((includeFlags & INCLUDE_BRANCHES) > 0) {
+ children.add(branches);
+ }
+ if ((includeFlags & INCLUDE_VERSIONS) > 0) {
+ children.add(versions);
+ }
+ if ((includeFlags & INCLUDE_DATES) > 0) {
+ children.add(dates);
+ }
+ return children.toArray(new Object[children.size()]);
+ }
+ public int getIncludeFlags() {
+ return includeFlags;
+ }
+ public TagRootElement getBranches() {
+ return branches;
+ }
+ public TagRootElement getVersions() {
+ return versions;
+ }
+ public TagRootElement getDates(){
+ return dates;
+ }
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class) return this;
+ return null;
+ }
+ public ImageDescriptor getImageDescriptor(Object object) {
+ return null;
+ }
+ public String getLabel(Object o) {
+ return null;
+ }
+ public Object getParent(Object o) {
+ return null;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizard.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizard.java
new file mode 100644
index 000000000..e7e527a57
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizard.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.team.internal.ccvs.core.CVSMergeSubscriber;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.core.client.Command;
+import org.eclipse.team.internal.ccvs.core.client.Update;
+import org.eclipse.team.internal.ccvs.ui.*;
+import org.eclipse.team.internal.ccvs.ui.operations.UpdateOperation;
+import org.eclipse.team.internal.ccvs.ui.subscriber.MergeSynchronizeParticipant;
+import org.eclipse.team.internal.ccvs.ui.tags.TagSource;
+import org.eclipse.team.ui.TeamUI;
+import org.eclipse.team.ui.synchronize.ISynchronizeParticipant;
+import org.eclipse.ui.IWorkbenchPart;
+
+public class MergeWizard extends Wizard {
+ MergeWizardPage page;
+ IResource[] resources;
+ private final IWorkbenchPart part;
+
+ public MergeWizard(IWorkbenchPart part, IResource[] resources) {
+ this.part = part;
+ this.resources = resources;
+ }
+
+ public void addPages() {
+ setNeedsProgressMonitor(true);
+ TagSource tagSource = TagSource.create(resources);
+ setWindowTitle(Policy.bind("MergeWizard.title")); //$NON-NLS-1$
+ ImageDescriptor mergeImage = CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_WIZBAN_MERGE);
+ page = new MergeWizardPage("mergePage", Policy.bind("MergeWizard.0"), mergeImage, Policy.bind("MergeWizard.1"), tagSource); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ addPage(page);
+ }
+
+ /*
+ * @see IWizard#performFinish()
+ */
+ public boolean performFinish() {
+
+ CVSTag startTag = page.getStartTag();
+ CVSTag endTag = page.getEndTag();
+
+ if (startTag == null || !page.isPreview()) {
+ // Perform the update (merge) in the background
+ UpdateOperation op = new UpdateOperation(getPart(), resources, getLocalOptions(startTag, endTag), null);
+ try {
+ op.run();
+ } catch (InvocationTargetException e) {
+ CVSUIPlugin.openError(getShell(), null, null, e);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ } else {
+ // First check if there is an existing matching participant, if so then re-use it
+ MergeSynchronizeParticipant participant = MergeSynchronizeParticipant.getMatchingParticipant(resources, startTag, endTag);
+ if(participant == null) {
+ CVSMergeSubscriber s = new CVSMergeSubscriber(resources, startTag, endTag);
+ participant = new MergeSynchronizeParticipant(s);
+ TeamUI.getSynchronizeManager().addSynchronizeParticipants(new ISynchronizeParticipant[] {participant});
+ }
+ participant.refresh(resources, null, null, null);
+ }
+ return true;
+ }
+
+ private Command.LocalOption[] getLocalOptions(CVSTag startTag, CVSTag endTag) {
+ List options = new ArrayList();
+ if (startTag != null) {
+ options.add(Command.makeArgumentOption(Update.JOIN, startTag.getName()));
+ }
+ options.add(Command.makeArgumentOption(Update.JOIN, endTag.getName()));
+ return (Command.LocalOption[]) options.toArray(new Command.LocalOption[options.size()]);
+ }
+
+ private IWorkbenchPart getPart() {
+ return part;
+ }
+}
diff --git a/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizardPage.java b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizardPage.java
new file mode 100644
index 000000000..c10125b69
--- /dev/null
+++ b/bundles/org.eclipse.team.cvs.ui/src/org/eclipse/team/internal/ccvs/ui/wizards/MergeWizardPage.java
@@ -0,0 +1,254 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ccvs.ui.wizards;
+
+import org.eclipse.jface.dialogs.Dialog;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.*;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.ui.IHelpContextIds;
+import org.eclipse.team.internal.ccvs.ui.Policy;
+import org.eclipse.team.internal.ccvs.ui.tags.*;
+
+public class MergeWizardPage extends CVSWizardPage {
+
+ private Text endTagField;
+ private Button endTagBrowseButton;
+ private TagSource tagSource;
+ private Text startTagField;
+ private Button startTagBrowseButton;
+ private TagRefreshButtonArea tagRefreshArea;
+ private CVSTag startTag;
+ private CVSTag endTag;
+ private Button previewButton;
+ private Button noPreviewButton;
+ protected boolean preview = true;
+
+ public MergeWizardPage(String pageName, String title, ImageDescriptor titleImage, String description, TagSource tagSource) {
+ super(pageName, title, titleImage, description);
+ this.tagSource = tagSource;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public void createControl(Composite parent) {
+ Composite composite = createComposite(parent, 1, true);
+
+ Composite mainArea = createComposite(composite, 2, true);
+ createEndTagArea(mainArea);
+ createStartTagArea(mainArea);
+ createPreviewOptionArea(mainArea);
+
+ createTagRefreshArea(composite);
+
+ Dialog.applyDialogFont(composite);
+ setControl(composite);
+ }
+
+ private void createPreviewOptionArea(Composite mainArea) {
+ previewButton = createRadioButton(mainArea, Policy.bind("MergeWizardPage.0"), 2); //$NON-NLS-1$
+ noPreviewButton = createRadioButton(mainArea, Policy.bind("MergeWizardPage.1"), 2); //$NON-NLS-1$
+ SelectionAdapter selectionAdapter = new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ preview = previewButton.getSelection();
+ updateEnablements();
+ }
+ };
+ previewButton.setSelection(preview);
+ noPreviewButton.setSelection(!preview);
+ previewButton.addSelectionListener(selectionAdapter);
+ noPreviewButton.addSelectionListener(selectionAdapter);
+ }
+
+ private void createTagRefreshArea(Composite parent) {
+ tagRefreshArea = new TagRefreshButtonArea(getShell(), getTagSource());
+ tagRefreshArea.setRunnableContext(getContainer());
+ tagRefreshArea.createArea(parent);
+ }
+
+ private void createEndTagArea(Composite parent) {
+ createWrappingLabel(parent, Policy.bind("MergeWizardPage.2"), 0, 2); //$NON-NLS-1$
+ endTagField = createTextField(parent);
+ endTagField.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ updateEndTag(endTagField.getText());
+ }
+ });
+ final int endTagIncludeFlags = TagSelectionArea.INCLUDE_VERSIONS | TagSelectionArea.INCLUDE_BRANCHES | TagSelectionArea.INCLUDE_HEAD_TAG;
+ TagContentAssistProcessor.createContentAssistant(endTagField, tagSource, endTagIncludeFlags);
+ endTagBrowseButton = createPushButton(parent, Policy.bind("MergeWizardPage.3")); //$NON-NLS-1$
+ endTagBrowseButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ TagSelectionDialog dialog = new TagSelectionDialog(getShell(), getTagSource(),
+ Policy.bind("MergeWizardPage.4"), //$NON-NLS-1$
+ Policy.bind("MergeWizardPage.5"), //$NON-NLS-1$
+ endTagIncludeFlags,
+ false, IHelpContextIds.MERGE_END_PAGE);
+ if (dialog.open() == Dialog.OK) {
+ CVSTag selectedTag = dialog.getResult();
+ setEndTag(selectedTag);
+ }
+ }
+ });
+ }
+
+ private void createStartTagArea(Composite parent) {
+ createWrappingLabel(parent, Policy.bind("MergeWizardPage.6"), 0, 2); //$NON-NLS-1$
+ startTagField = createTextField(parent);
+ startTagField.addModifyListener(new ModifyListener() {
+ public void modifyText(ModifyEvent e) {
+ updateStartTag(startTagField.getText());
+ }
+ });
+ TagContentAssistProcessor.createContentAssistant(startTagField, tagSource, TagSelectionArea.INCLUDE_VERSIONS);
+ startTagBrowseButton = createPushButton(parent, Policy.bind("MergeWizardPage.7")); //$NON-NLS-1$
+ startTagBrowseButton.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ TagSelectionDialog dialog = new TagSelectionDialog(getShell(), getTagSource(),
+ Policy.bind("MergeWizardPage.8"), //$NON-NLS-1$
+ Policy.bind("MergeWizardPage.9"), //$NON-NLS-1$
+ TagSelectionDialog.INCLUDE_VERSIONS,
+ false, IHelpContextIds.MERGE_START_PAGE);
+ if (dialog.open() == Dialog.OK) {
+ CVSTag selectedTag = dialog.getResult();
+ setStartTag(selectedTag);
+ }
+ }
+ });
+ }
+
+ protected void updateEndTag(String text) {
+ if (endTag == null || !endTag.getName().equals(text)) {
+ CVSTag tag = getTagFor(text, false);
+ setEndTag(tag);
+ }
+ updateEnablements();
+ }
+
+ protected void updateStartTag(String text) {
+ if (startTag == null || !startTag.getName().equals(text)) {
+ CVSTag tag = getTagFor(text, true);
+ setStartTag(tag);
+ }
+ updateEnablements();
+ }
+
+ private CVSTag getTagFor(String text, boolean versionsOnly) {
+ if (text.equals(CVSTag.DEFAULT.getName())) {
+ if (versionsOnly) return null;
+ return CVSTag.DEFAULT;
+ }
+ if (text.equals(CVSTag.BASE.getName())) {
+ if (versionsOnly) return null;
+ return CVSTag.BASE;
+ }
+ CVSTag[] tags;
+ if (versionsOnly) {
+ tags = tagSource.getTags(new int[] { CVSTag.VERSION, CVSTag.DATE });
+ } else {
+ tags = tagSource.getTags(new int[] { CVSTag.VERSION, CVSTag.BRANCH, CVSTag.DATE });
+ }
+ for (int i = 0; i < tags.length; i++) {
+ CVSTag tag = tags[i];
+ if (tag.getName().equals(text)) {
+ return tag;
+ }
+ }
+ return null;
+ }
+
+ protected void setEndTag(CVSTag selectedTag) {
+ if (selectedTag == null || endTag == null || !endTag.equals(selectedTag)) {
+ endTag = selectedTag;
+ if (endTagField != null) {
+ String name = endTagField.getText();
+ if (endTag != null)
+ name = endTag.getName();
+ if (!endTagField.getText().equals(name))
+ endTagField.setText(name);
+ if (startTag == null && endTag != null && endTag.getType() == CVSTag.BRANCH) {
+ CVSTag tag = findCommonBaseTag(endTag);
+ if (tag != null) {
+ setStartTag(tag);
+ }
+ }
+ }
+ updateEnablements();
+ }
+ }
+
+ protected void setStartTag(CVSTag selectedTag) {
+ if (selectedTag == null || startTag != null || !endTag.equals(selectedTag)) {
+ startTag = selectedTag;
+ if (startTagField != null) {
+ String name = startTagField.getText();
+ if (startTag != null)
+ name = startTag.getName();
+ if (!startTagField.getText().equals(name))
+ startTagField.setText(name);
+ }
+ updateEnablements();
+ }
+ }
+
+ private CVSTag findCommonBaseTag(CVSTag tag) {
+ CVSTag[] tags = tagSource.getTags(CVSTag.VERSION);
+ for (int i = 0; i < tags.length; i++) {
+ CVSTag potentialMatch = tags[i];
+ if (potentialMatch.getName().indexOf(tag.getName()) != -1) {
+ return potentialMatch;
+ }
+ }
+ return null;
+ }
+
+ private void updateEnablements() {
+ if (endTag == null && endTagField.getText().length() > 0) {
+ setErrorMessage(Policy.bind("MergeWizardPage.10")); //$NON-NLS-1$
+ } else if (startTag == null && startTagField.getText().length() > 0) {
+ setErrorMessage(Policy.bind("MergeWizardPage.11")); //$NON-NLS-1$
+ } else if (endTag != null && startTag != null && startTag.equals(endTag)) {
+ setErrorMessage(Policy.bind("MergeWizardPage.12")); //$NON-NLS-1$
+ } else if (startTag == null && endTag != null && preview) {
+ setErrorMessage(Policy.bind("MergeWizardPage.13")); //$NON-NLS-1$
+ } else {
+ setErrorMessage(null);
+ }
+ setPageComplete((startTag != null || !preview) && endTag != null && (startTag == null || !startTag.equals(endTag)));
+ }
+
+ protected TagSource getTagSource() {
+ return tagSource;
+ }
+
+ private Button createPushButton(Composite parent, String label) {
+ Button b = new Button(parent, SWT.PUSH);
+ b.setText(label);
+ b.setLayoutData(new GridData(GridData.END | GridData.HORIZONTAL_ALIGN_FILL));
+ return b;
+ }
+
+ public CVSTag getStartTag() {
+ return startTag;
+ }
+
+ public CVSTag getEndTag() {
+ return endTag;
+ }
+
+ public boolean isPreview() {
+ return preview;
+ }
+}
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
new file mode 100644
index 000000000..36ad7cc92
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/EclipseTest.java
@@ -0,0 +1,1010 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.tests.ccvs.core;
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+
+import junit.framework.*;
+
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.tests.resources.ResourceTest;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.TeamException;
+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.core.connection.*;
+import org.eclipse.team.internal.ccvs.core.resources.*;
+import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
+import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
+import org.eclipse.team.internal.ccvs.core.util.SyncFileChangeListener;
+import org.eclipse.team.internal.ccvs.ui.operations.*;
+import org.eclipse.team.internal.core.subscribers.SubscriberSyncInfoCollector;
+import org.eclipse.ui.IWorkbenchPart;
+
+public class EclipseTest extends ResourceTest {
+
+ protected static IProgressMonitor DEFAULT_MONITOR = new NullProgressMonitor();
+ protected static final int RANDOM_CONTENT_SIZE = 3876;
+ protected static String eol = System.getProperty("line.separator");
+
+ public static Test suite(Class c) {
+ String testName = System.getProperty("eclipse.cvs.testName");
+ if (testName == null) {
+ TestSuite suite = new TestSuite(c);
+ return new CVSTestSetup(suite);
+ } else {
+ try {
+ return new CVSTestSetup((Test)c.getConstructor(new Class[] { String.class }).newInstance(new Object[] {testName}));
+ } catch (Exception e) {
+ fail(e.getMessage());
+ // Above will throw so below is never actually reached
+ return null;
+ }
+ }
+ }
+
+ public EclipseTest() {
+ super();
+ if (eol == null) eol = "\n";
+ }
+
+ public EclipseTest(String name) {
+ super(name);
+ if (eol == null) eol = "\n";
+ }
+
+ /*
+ * Get the resources for the given resource names
+ */
+ public IResource[] getResources(IContainer container, String[] hierarchy) throws CoreException {
+ IResource[] resources = new IResource[hierarchy.length];
+ for (int i=0;i<resources.length;i++) {
+ resources[i] = container.findMember(hierarchy[i]);
+ if (resources[i] == null) {
+ resources[i] = buildResources(container, new String[] {hierarchy[i]})[0];
+ }
+ }
+ return resources;
+ }
+
+ /**
+ * Add the resources to an existing container and upload them to CVS
+ */
+ public IResource[] addResources(IContainer container, String[] hierarchy, boolean checkin) throws CoreException, TeamException {
+ IResource[] newResources = buildResources(container, hierarchy, false);
+ addResources(newResources);
+ if (checkin) commitResources(newResources, IResource.DEPTH_ZERO);
+ return newResources;
+ }
+
+ protected void addResources(IResource[] newResources) throws CoreException {
+ if (newResources.length == 0) return;
+ executeHeadless(new AddOperation(null, newResources));
+ }
+
+ /**
+ * Perform a CVS edit of the given resources
+ */
+ public IResource[] editResources(IContainer container, String[] hierarchy) throws CoreException, TeamException {
+ IResource[] resources = getResources(container, hierarchy);
+ getProvider(container).edit(resources, true /* recurse */, true /* notifyServer */, ICVSFile.NO_NOTIFICATION, DEFAULT_MONITOR);
+ assertReadOnly(resources, false /* isReadOnly */, true /* recurse */);
+ return resources;
+ }
+
+ /**
+ * Perform a CVS unedit of the given resources
+ */
+ public IResource[] uneditResources(IContainer container, String[] hierarchy) throws CoreException, TeamException {
+ IResource[] resources = getResources(container, hierarchy);
+ getProvider(container).unedit(resources, true /* recurse */, true/* notifyServer */, DEFAULT_MONITOR);
+ assertReadOnly(resources, true /* isReadOnly */, true /* recurse */);
+ return resources;
+ }
+
+ public void appendText(IResource resource, String text, boolean prepend) throws CoreException, IOException, CVSException {
+ IFile file = (IFile)resource;
+ String contents = getFileContents(file);
+ StringBuffer buffer = new StringBuffer();
+ if (prepend) {
+ buffer.append(text);
+ }
+ buffer.append(contents);
+ if (!prepend) {
+ buffer.append(eol + text);
+ }
+ setContentsAndEnsureModified(file, buffer.toString());
+ }
+
+ public void assertEndsWith(IFile file, String text) throws IOException, CoreException {
+ assertTrue(getFileContents(file).endsWith(text));
+ }
+
+ public void assertStartsWith(IFile file, String text) throws IOException, CoreException {
+ assertTrue(getFileContents(file).startsWith(text));
+ }
+
+ public static String getFileContents(IFile file) throws IOException, CoreException {
+ StringBuffer buf = new StringBuffer();
+ Reader reader = new InputStreamReader(new BufferedInputStream(file.getContents()));
+ try {
+ int c;
+ while ((c = reader.read()) != -1) buf.append((char)c);
+ } finally {
+ reader.close();
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Delete the resources from an existing container and the changes to CVS
+ */
+ public IResource[] changeResources(IContainer container, String[] hierarchy, boolean checkin) throws CoreException, TeamException {
+ List changedResources = new ArrayList(hierarchy.length);
+ for (int i=0;i<hierarchy.length;i++) {
+ IResource resource = container.findMember(hierarchy[i]);
+ if (resource.getType() == IResource.FILE) {
+ changedResources.add(resource);
+ setContentsAndEnsureModified((IFile)resource);
+ }
+ }
+ IResource[] resources = (IResource[])changedResources.toArray(new IResource[changedResources.size()]);
+ if (checkin) commitResources(resources, IResource.DEPTH_ZERO);
+ return resources;
+ }
+
+ /**
+ * Delete the resources from an existing container and the changes to CVS
+ */
+ public IResource[] deleteResources(IContainer container, String[] hierarchy, boolean checkin) throws CoreException, TeamException {
+ IResource[] resources = getResources(container, hierarchy);
+ deleteResources(resources);
+ if (checkin)
+ commitResources(resources, IResource.DEPTH_ZERO);
+ return resources;
+ }
+
+ /**
+ * Delete the resources and mark them as outgoing deletions.
+ * Deleting the resources is enough since the move/delete hook will
+ * tak care of making them outgoing deletions.
+ */
+ protected void deleteResources(IResource[] resources) throws TeamException, CoreException {
+ if (resources.length == 0) return;
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ resource.delete(false, DEFAULT_MONITOR);
+ }
+ }
+ /**
+ * Unmanage the resources
+ */
+ public void unmanageResources(IContainer container, String[] hierarchy) throws CoreException, TeamException {
+ IResource[] resources = getResources(container, hierarchy);
+ unmanageResources(resources);
+ }
+
+ protected void unmanageResources(IResource[] resources) throws TeamException, CoreException {
+ for (int i=0;i<resources.length;i++) {
+ CVSWorkspaceRoot.getCVSResourceFor(resources[i]).unmanage(null);
+ }
+ }
+
+ /**
+ * Update the resources from an existing container with the changes from the CVS repository
+ */
+ public IResource[] updateResources(IContainer container, String[] hierarchy, boolean ignoreLocalChanges) throws CoreException, TeamException {
+ IResource[] resources = getResources(container, hierarchy);
+ return updateResources(resources, ignoreLocalChanges);
+ }
+
+ /**
+ * Update the resources from an existing container with the changes from the CVS repository
+ */
+ protected IResource[] updateResources(IResource[] resources, boolean ignoreLocalChanges) throws CVSException {
+ LocalOption[] options = Command.NO_LOCAL_OPTIONS;
+ if(ignoreLocalChanges) {
+ options = new LocalOption[] {Update.IGNORE_LOCAL_CHANGES};
+ }
+ executeHeadless(new UpdateOperation(null, resources, options, null));
+ return resources;
+ }
+
+ protected void replace(IContainer container, String[] hierarchy, CVSTag tag, boolean recurse) throws CoreException {
+ IResource[] resources = getResources(container, hierarchy);
+ replace(resources, tag, recurse);
+ }
+
+ protected void replace(IResource[] resources, CVSTag tag, boolean recurse) throws CoreException {
+ ReplaceOperation op = new ReplaceOperation(null, resources, tag, recurse);
+ executeHeadless(op);
+ }
+
+ public void updateProject(IProject project, CVSTag tag, boolean ignoreLocalChanges) throws TeamException {
+ LocalOption[] options = Command.NO_LOCAL_OPTIONS;
+ if(ignoreLocalChanges) {
+ options = new LocalOption[] {Update.IGNORE_LOCAL_CHANGES};
+ }
+ executeHeadless(new UpdateOperation(null, new IResource[] {project}, options, tag));
+ }
+
+ public void commitProject(IProject project) throws TeamException, CoreException {
+ commitResources(project, true);
+ }
+
+ public void commitResources(IContainer container, boolean deep) throws TeamException, CoreException {
+ commitResources(new IResource[] {container }, deep?IResource.DEPTH_INFINITE:IResource.DEPTH_ZERO);
+ }
+
+ /**
+ * Commit the resources from an existing container to the CVS repository
+ */
+ public IResource[] commitResources(IContainer container, String[] hierarchy) throws CoreException, TeamException {
+ IResource[] resources = getResources(container, hierarchy);
+ commitResources(resources, IResource.DEPTH_ZERO);
+ return resources;
+ }
+
+ protected void commitResources(IResource[] resources, int depth) throws TeamException, CoreException {
+ commitResources(resources, depth, "");
+ }
+
+ /*
+ * Commit the provided resources which must all be in the same project
+ */
+ protected void commitResources(IResource[] resources, int depth, String message) throws TeamException, CoreException {
+ if (resources.length == 0) return;
+ executeHeadless(new CommitOperation(null, resources, new Command.LocalOption[] { Commit.makeArgumentOption(Command.MESSAGE_OPTION, message) }));
+ }
+
+ /**
+ * Commit the resources from an existing container to the CVS repository
+ */
+ public void tagProject(IProject project, CVSTag tag, boolean force) throws TeamException {
+ ITagOperation op = new TagOperation((IWorkbenchPart)null, new IResource[] {project});
+ runTag(op, tag, force);
+ }
+
+ public void tagRemoteResource(ICVSRemoteResource resource, CVSTag tag, boolean force) throws TeamException {
+ ITagOperation op = new TagInRepositoryOperation(null, new ICVSRemoteResource[] {resource});
+ runTag(op, tag, force);
+
+ }
+ private void runTag(ITagOperation op, CVSTag tag, boolean force) throws TeamException {
+ if (force) op.moveTag();
+ op.setTag(tag);
+ try {
+ ((CVSOperation)op).run(DEFAULT_MONITOR);
+ } catch (InterruptedException e) {
+ fail("Tag interrupted.");
+ } catch (InvocationTargetException e) {
+ if (e.getTargetException() instanceof TeamException) {
+ throw (TeamException) e.getTargetException();
+ } else {
+ e.printStackTrace();
+ fail("Unexpected error while tagging");
+ }
+ }
+ }
+ public void makeBranch(IResource[] resources, CVSTag version, CVSTag branch, boolean update) throws CVSException {
+ BranchOperation op = new BranchOperation(null, resources);
+ op.setTags(version, branch, update);
+ executeHeadless(op);
+ }
+ /**
+ * Return a collection of resources defined by hierarchy. The resources
+ * are added to the workspace and to the file system. If the manage flag is true, the
+ * resources are auto-managed, if false, they are left un-managed.
+ */
+ public IResource[] buildResources(IContainer container, String[] hierarchy, boolean includeContainer) throws CoreException {
+ List resources = new ArrayList(hierarchy.length + 1);
+ resources.addAll(Arrays.asList(buildResources(container, hierarchy)));
+ if (includeContainer)
+ resources.add(container);
+ IResource[] result = (IResource[]) resources.toArray(new IResource[resources.size()]);
+ ensureExistsInWorkspace(result, true);
+ for (int i = 0; i < result.length; i++) {
+ if (result[i].getType() == IResource.FILE)
+ // 3786 bytes is the average size of Eclipse Java files!
+ ((IFile) result[i]).setContents(getRandomContents(RANDOM_CONTENT_SIZE), true, false, null);
+ }
+ return result;
+ }
+
+ /*
+ * Checkout a copy of the project into a project with the given postfix
+ */
+ protected IProject checkoutCopy(IProject project, String postfix) throws TeamException {
+ // Check the project out under a different name and validate that the results are the same
+ IProject copy = getWorkspace().getRoot().getProject(project.getName() + postfix);
+ checkout(getRepository(), copy, CVSWorkspaceRoot.getCVSFolderFor(project).getFolderSyncInfo().getRepository(), null, DEFAULT_MONITOR);
+ return copy;
+ }
+
+ protected IProject checkoutCopy(IProject project, CVSTag tag) throws TeamException {
+ // Check the project out under a different name and validate that the results are the same
+ IProject copy = getWorkspace().getRoot().getProject(project.getName() + tag.getName());
+ checkout(getRepository(), copy,
+ CVSWorkspaceRoot.getCVSFolderFor(project).getFolderSyncInfo().getRepository(),
+ tag, DEFAULT_MONITOR);
+ return copy;
+ }
+
+ public static void checkout(
+ final ICVSRepositoryLocation repository,
+ final IProject project,
+ final String sourceModule,
+ final CVSTag tag,
+ IProgressMonitor monitor)
+ throws TeamException {
+
+ RemoteFolder remote = new RemoteFolder(null, repository, sourceModule == null ? project.getName() : sourceModule, tag);
+ executeHeadless(new CheckoutSingleProjectOperation(null, remote, project, null, false /* the project is not preconfigured */) {
+ public boolean promptToOverwrite(String title, String msg) {
+ return true;
+ }
+ });
+
+ }
+
+ protected IProject checkoutProject(IProject project, String moduleName, CVSTag tag) throws TeamException {
+ if (project == null)
+ project = getWorkspace().getRoot().getProject(new Path(moduleName).lastSegment());
+ checkout(getRepository(), project, moduleName, tag, DEFAULT_MONITOR);
+ return project;
+ }
+ /*
+ * This method creates a project with the given resources, imports
+ * it to CVS and checks it out
+ */
+ protected IProject createProject(String prefix, String[] resources) throws CoreException, TeamException {
+ IProject project = getUniqueTestProject(prefix);
+ buildResources(project, resources, true);
+ shareProject(project);
+ assertValidCheckout(project);
+ return project;
+ }
+
+ /*
+ * Create a test project using the currently running test case as the project name prefix
+ */
+ protected IProject createProject(String[] strings) throws CoreException {
+ return createProject(getName(), strings);
+ }
+
+ /*
+ * Compare two projects by comparing thier providers
+ */
+ protected void assertEquals(IProject project1, IProject project2) throws CoreException, IOException {
+ assertEquals(project1, project2, false, false);
+ }
+
+ protected void assertEquals(IProject project1, IProject project2, boolean includeTimestamps, boolean includeTags) throws CoreException, IOException {
+ assertEquals(getProvider(project1), getProvider(project2), includeTimestamps, includeTags);
+ }
+
+ /*
+ * Compare CVS team providers by comparing the cvs resource corresponding to the provider's project
+ */
+ protected void assertEquals(CVSTeamProvider provider1, CVSTeamProvider provider2, boolean includeTimestamps, boolean includeTags) throws CoreException, IOException {
+ assertEquals(Path.EMPTY, CVSWorkspaceRoot.getCVSFolderFor(provider1.getProject()),
+ CVSWorkspaceRoot.getCVSFolderFor(provider2.getProject()),
+ includeTimestamps, includeTags);
+ }
+
+ protected void assertContentsEqual(IContainer c1, IContainer c2) throws CoreException {
+ assertTrue("The number of resource in " + c1.getProjectRelativePath().toString() + " differs",
+ c1.members().length == c2.members().length);
+ IResource[] resources = c1.members();
+ for (int i= 0;i <resources.length;i++) {
+ assertContentsEqual(resources[i], c2.findMember(resources[i].getName()));
+ }
+ }
+
+ protected void assertContentsEqual(IResource resource, IResource resource2) throws CoreException {
+ if (resource.getType() == IResource.FILE) {
+ assertContentsEqual((IFile)resource, (IFile)resource2);
+ } else {
+ assertContentsEqual((IContainer)resource, (IContainer)resource2);
+ }
+ }
+
+ protected void assertContentsEqual(IFile resource, IFile resource2) throws CoreException {
+ assertTrue("Contents of " + resource.getProjectRelativePath() + " do not match", compareContent(resource.getContents(), resource2.getContents()));
+ }
+ /*
+ * Compare resources by casting them to their prpoer type
+ */
+ protected void assertEquals(IPath parent, ICVSResource resource1, ICVSResource resource2, boolean includeTimestamps, boolean includeTags) throws CoreException, CVSException, IOException {
+ assertEquals("Resource types do not match for " + parent.append(resource1.getName()), resource1.isFolder(), resource2.isFolder());
+ if (!resource1.isFolder())
+ assertEquals(parent, (ICVSFile)resource1, (ICVSFile)resource2, includeTimestamps, includeTags);
+ else
+ assertEquals(parent, (ICVSFolder)resource1, (ICVSFolder)resource2, includeTimestamps, includeTags);
+ }
+
+ /*
+ * Compare folders by comparing their folder sync info and there children
+ *
+ * XXX What about unmanaged children?
+ */
+ protected void assertEquals(IPath parent, ICVSFolder container1, ICVSFolder container2, boolean includeTimestamps, boolean includeTags) throws CoreException, CVSException, IOException {
+ IPath path = parent.append(container1.getName());
+ assertEquals(path, container1.getFolderSyncInfo(), container2.getFolderSyncInfo(), includeTags);
+ assertTrue("The number of resource in " + path.toString() + " differs",
+ container1.members(ICVSFolder.ALL_EXISTING_MEMBERS).length
+ == container2.members(ICVSFolder.ALL_EXISTING_MEMBERS).length);
+ ICVSResource[] resources = container1.members(ICVSFolder.ALL_EXISTING_MEMBERS);
+ for (int i= 0;i <resources.length;i++) {
+ assertEquals(path, resources[i], container2.getChild(resources[i].getName()), includeTimestamps, includeTags);
+ }
+
+ }
+
+ /*
+ * Compare the files contents and sync information
+ */
+ protected void assertEquals(IPath parent, ICVSFile file1, ICVSFile file2, boolean includeTimestamps, boolean includeTags) throws CoreException, CVSException, IOException {
+ if (file1.getName().equals(".project")) return;
+ // Getting the contents first is important as it will fetch the proper sync info if one of the files is a remote handle
+ assertTrue("Contents of " + parent.append(file1.getName()) + " do not match", compareContent(getContents(file1), getContents(file2)));
+ assertEquals(parent.append(file1.getName()), file1.getSyncInfo(), file2.getSyncInfo(), includeTimestamps, includeTags);
+ }
+
+ /*
+ * Compare sync info by comparing the entry line generated by the sync info
+ */
+ protected void assertEquals(IPath path, ResourceSyncInfo info1, ResourceSyncInfo info2, boolean includeTimestamp, boolean includeTag) throws CoreException, CVSException, IOException {
+ if (info1 == null || info2 == null) {
+ if (info1 == info2) return;
+ if (info1 == null) {
+ fail("Expected no resource sync info for " + path.toString() + " but it was " + info2 + " instead");
+ }
+ if (info2 == null) {
+ fail("Expected resource sync info of " + info1 + " for " + path.toString() + " but there was no sync info.");
+ }
+ fail("Shouldn't be able to get here");
+ return;
+ }
+ String line1;
+ String line2;
+ if(includeTimestamp) {
+ line1 = info1.getEntryLine();
+ line2 = info2.getEntryLine();
+ } else {
+ line1 = info1.getServerEntryLine(null);
+ line2 = info2.getServerEntryLine(null);
+ }
+ if (!includeTag) {
+ // Strip everything past the last slash
+ line1 = line1.substring(0, line1.lastIndexOf('/'));
+ line2 = line2.substring(0, line2.lastIndexOf('/'));
+ }
+ assertEquals("Resource Sync info differs for " + path.toString(), line1, line2);
+ }
+
+ /*
+ * Use the equals of folder sync info unless the tag is not included in which case we just
+ * compare the root and repository
+ */
+ protected void assertEquals(IPath path, FolderSyncInfo info1, FolderSyncInfo info2, boolean includeTag) throws CoreException, CVSException, IOException {
+ if (info1 == null && info2 == null) {
+ return;
+ } else if (info1 == null) {
+ fail("Expected " + path.toString() + " not to be a CVS folder but it is.");
+ } else if (info2 == null) {
+ fail("Expected " + path.toString() + " to be a CVS folder but it isn't.");
+ }
+
+ if (includeTag) {
+ assertTrue("Folder sync info differs for " + path.toString(), info1.equals(info2));
+ } else {
+ assertTrue("Repository Root differs for " + path.toString(), info1.getRoot().equals(info2.getRoot()));
+ assertTrue("Repository relative path differs for " + path.toString(), info1.getRepository().equals(info2.getRepository()));
+ }
+ }
+
+
+ /*
+ * Compare folders by comparing their folder sync info and there children
+ *
+ * XXX What about unmanaged children?
+ */
+ protected void assertEquals(IPath parent, RemoteFolder container1, RemoteFolder container2, boolean includeTags) throws CoreException, TeamException, IOException {
+ IPath path = parent.append(container1.getName());
+ assertEquals(path, container1.getFolderSyncInfo(), container2.getFolderSyncInfo(), includeTags);
+ ICVSRemoteResource[] members1 = container1.getMembers(DEFAULT_MONITOR);
+ ICVSRemoteResource[] members2 = container2.getMembers(DEFAULT_MONITOR);
+ assertTrue("Number of members differ for " + path, members1.length == members2.length);
+ Map memberMap2 = new HashMap();
+ for (int i= 0;i <members2.length;i++) {
+ memberMap2.put(members2[i].getName(), members2[i]);
+ }
+ for (int i= 0;i <members1.length;i++) {
+ ICVSRemoteResource member2 = (ICVSRemoteResource)memberMap2.get(members1[i].getName());
+ assertNotNull("Resource does not exist: " + path.append(members1[i].getName()) + member2);
+ assertEquals(path, members1[i], member2, includeTags);
+ }
+ }
+ protected void assertEquals(IPath parent, ICVSRemoteResource resource1, ICVSRemoteResource resource2, boolean includeTags) throws CoreException, TeamException, IOException {
+ assertEquals("Resource types do not match for " + parent.append(resource1.getName()), resource1.isContainer(), resource2.isContainer());
+ if (resource1.isContainer())
+ assertEquals(parent, (RemoteFolder)resource1, (RemoteFolder)resource2, includeTags);
+ else
+ assertEquals(parent, (ICVSFile)resource1, (ICVSFile)resource2, false, includeTags);
+ }
+
+
+ /*
+ * Compare the local project with the remote state by checking out a copy of the project.
+ */
+ protected void assertLocalStateEqualsRemote(IProject project) throws TeamException, CoreException, IOException {
+ assertEquals(getProvider(project), getProvider(checkoutCopy(project, "-remote")), false, true);
+ }
+
+ /*
+ * Compare the local project with the remote state indicated by the given tag by checking out a copy of the project.
+ */
+ protected void assertLocalStateEqualsRemote(String message, IProject project, CVSTag tag) throws TeamException, CoreException, IOException {
+ assertEquals(getProvider(project), getProvider(checkoutCopy(project, tag)), true, false);
+ }
+
+ protected void assertHasNoRemote(String prefix, IResource[] resources) throws TeamException {
+ for (int i=0;i<resources.length;i++)
+ assertHasNoRemote(prefix, resources[i]);
+ }
+
+ protected void assertHasNoRemote(String prefix, IResource resource) throws TeamException {
+ assertTrue(prefix + " resource should not have a remote", !CVSWorkspaceRoot.hasRemote(resource));
+ }
+
+ protected void assertHasRemote(String prefix, IResource[] resources) throws TeamException {
+ for (int i=0;i<resources.length;i++)
+ assertHasRemote(prefix, resources[i]);
+ }
+
+ protected void assertHasRemote(String prefix, IResource resource) throws TeamException {
+ assertTrue(prefix + " resource should have a remote", CVSWorkspaceRoot.hasRemote(resource));
+ }
+
+ protected void assertIsModified(String prefix, IResource[] resources) throws TeamException {
+ for (int i=0;i<resources.length;i++)
+ assertIsModified(prefix, resources[i]);
+ }
+
+ protected void assertIsModified(String prefix, IResource resource) throws TeamException {
+ // Only check for files as CVS doesn't dirty folders
+ if (resource.getType() == IResource.FILE)
+ assertTrue(prefix + " resource " + resource.getFullPath() + " should be dirty.", ((ICVSFile)getCVSResource(resource)).isModified(null));
+ }
+
+ protected void assertNotModified(String prefix, IResource[] resources) throws TeamException {
+ for (int i=0;i<resources.length;i++)
+ assertNotModified(prefix, resources[i]);
+ }
+
+ protected void assertNotModified(String prefix, IResource resource) throws TeamException {
+ assertTrue(prefix + " resource should be dirty", !((ICVSFile)getCVSResource(resource)).isModified(null));
+ }
+
+ protected void assertIsIgnored(IResource resource, boolean ignoredState) throws TeamException {
+ assertEquals("Resource " + resource.getFullPath() + " should be ignored but isn't.",
+ ignoredState, getCVSResource(resource).isIgnored());
+ }
+
+ protected void assertValidCheckout(IProject project) {
+ // NOTE: Add code to ensure that the project was checkout out properly
+ CVSTeamProvider provider = (CVSTeamProvider)RepositoryProvider.getProvider(project);
+ assertNotNull(provider);
+ }
+
+ protected void assertReadOnly(IResource[] resources, final boolean isReadOnly, final boolean recurse) throws CoreException {
+ for (int i = 0; i < resources.length; i++) {
+ IResource resource = resources[i];
+ resource.accept(new IResourceVisitor() {
+ public boolean visit(IResource resource) throws CoreException {
+ if (resource.getType() == IResource.FILE) {
+ assertEquals(isReadOnly, resource.isReadOnly());
+ }
+ return recurse;
+ }
+ });
+ }
+ }
+
+ protected InputStream getContents(ICVSFile file) throws CVSException, IOException {
+ if (file instanceof ICVSRemoteFile)
+ return ((RemoteFile)file).getContents(DEFAULT_MONITOR);
+ else
+ return new BufferedInputStream(file.getContents());
+ }
+
+ /*
+ * Get the CVS Resource for the given resource
+ */
+ protected ICVSResource getCVSResource(IResource resource) throws CVSException {
+ return CVSWorkspaceRoot.getCVSResourceFor(resource);
+ }
+
+ protected IProject getNamedTestProject(String name) throws CoreException {
+ IProject target = getWorkspace().getRoot().getProject(name);
+ if (!target.exists()) {
+ target.create(null);
+ target.open(null);
+ }
+ assertExistsInFileSystem(target);
+ return target;
+ }
+ protected CVSTeamProvider getProvider(IResource resource) throws TeamException {
+ return (CVSTeamProvider)RepositoryProvider.getProvider(resource.getProject());
+ }
+ protected static InputStream getRandomContents(int sizeAtLeast) {
+ StringBuffer randomStuff = new StringBuffer(sizeAtLeast + 100);
+ while (randomStuff.length() < sizeAtLeast) {
+ randomStuff.append(getRandomSnippet() + eol);
+ }
+ return new ByteArrayInputStream(randomStuff.toString().getBytes());
+ }
+ /**
+ * Return String with some random text to use
+ * as contents for a file resource.
+ */
+ public static String getRandomSnippet() {
+ switch ((int) Math.round(Math.random() * 10)) {
+ case 0 :
+ return "este e' o meu conteudo (portuguese)";
+ case 1 :
+ return "Dann brauchen wir aber auch einen deutschen Satz!";
+ case 2 :
+ return "I'll be back";
+ case 3 :
+ return "don't worry, be happy";
+ case 4 :
+ return "there is no imagination for more sentences";
+ case 5 :
+ return "customize yours";
+ case 6 :
+ return "foo";
+ case 7 :
+ return "bar";
+ case 8 :
+ return "foobar";
+ case 9 :
+ return "case 9";
+ default :
+ return "these are my contents";
+ }
+ }
+ protected IProject getUniqueTestProject(String prefix) throws CoreException {
+ // manage and share with the default stream create by this class
+ return getNamedTestProject(prefix + "-" + Long.toString(System.currentTimeMillis()));
+ }
+
+ protected CVSRepositoryLocation getRepository() {
+ return CVSTestSetup.repository;
+ }
+ protected void importProject(IProject project) throws TeamException {
+
+ // Create the root folder for the import operation
+ ICVSFolder root = CVSWorkspaceRoot.getCVSFolderFor(project);
+
+ // Perform the import
+ IStatus status;
+ Session s = new Session(getRepository(), root);
+ s.open(DEFAULT_MONITOR, true /* open for modification */);
+ try {
+ status = Command.IMPORT.execute(s,
+ Command.NO_GLOBAL_OPTIONS,
+ new LocalOption[] {Import.makeArgumentOption(Command.MESSAGE_OPTION, "Initial Import")},
+ new String[] { project.getName(), getRepository().getUsername(), "start" },
+ null,
+ DEFAULT_MONITOR);
+ } finally {
+ s.close();
+ }
+
+ if (status.getCode() == CVSStatus.SERVER_ERROR) {
+ throw new CVSServerException(status);
+ }
+ }
+
+ protected void shareProject(IProject project) throws TeamException, CoreException {
+ mapNewProject(project);
+ commitNewProject(project);
+ }
+
+ protected void mapNewProject(IProject project) throws TeamException {
+ shareProject(getRepository(), project, null, DEFAULT_MONITOR);
+ }
+
+ /**
+ * Map the given local project to remote folder, creating the remote folder or any of
+ * its ancestors as necessary.
+ * @param location
+ * @param project
+ * @param moduleName
+ * @param default_monitor
+ */
+ protected void shareProject(CVSRepositoryLocation location, IProject project, String moduleName, IProgressMonitor default_monitor) throws CVSException {
+ ShareProjectOperation op = new ShareProjectOperation(null, location, project, moduleName);
+ executeHeadless(op);
+ }
+
+ protected void commitNewProject(IProject project) throws CoreException, CVSException, TeamException {
+ List resourcesToAdd = new ArrayList();
+ IResource[] members = project.members();
+ for (int i = 0; i < members.length; i++) {
+ if ( ! CVSWorkspaceRoot.getCVSResourceFor(members[i]).isIgnored()) {
+ resourcesToAdd.add(members[i]);
+ }
+ }
+ addResources((IResource[]) resourcesToAdd.toArray(new IResource[resourcesToAdd.size()]));
+ commitResources(new IResource[] {project}, IResource.DEPTH_INFINITE);
+ // Pause to ensure that future operations happen later than timestamp of committed resources
+ waitMsec(1500);
+ }
+
+ /**
+ * Return an input stream with some random text to use
+ * as contents for a file resource.
+ */
+ public InputStream getRandomContents() {
+ return getRandomContents(RANDOM_CONTENT_SIZE);
+ }
+
+ protected void setContentsAndEnsureModified(IFile file) throws CoreException, TeamException {
+ setContentsAndEnsureModified(file, getRandomContents().toString());
+ }
+
+ protected void setContentsAndEnsureModified(IFile file, String contents) 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);
+ assertTrue("Timestamp granularity is too small. Increase test wait factor", count <= CVSTestSetup.WAIT_FACTOR);
+ if (!cvsFile.isModified(null)) {
+ waitMsec(1500);
+ count++;
+ }
+ } while (!cvsFile.isModified(null));
+ }
+
+ public void waitMsec(int msec) {
+ try {
+ Thread.sleep(msec);
+ } catch(InterruptedException e) {
+ fail("wait-problem");
+ }
+ }
+
+ public static void waitForJobCompletion(Job job) {
+ // process UI events first, give the main thread a chance
+ // to handle any syncExecs or asyncExecs posted as a result
+ // of the event processing thread.
+ while (Display.getCurrent().readAndDispatch()) {};
+
+ // wait for the event handler to process changes.
+ while(job.getState() != Job.NONE) {
+ while (Display.getCurrent().readAndDispatch()) {};
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ }
+ }
+ while (Display.getCurrent().readAndDispatch()) {};
+ }
+
+ public static void waitForIgnoreFileHandling() {
+ waitForJobCompletion(SyncFileChangeListener.getDeferredHandler().getEventHandlerJob());
+ }
+
+ public static void waitForSubscriberInputHandling(SubscriberSyncInfoCollector input) {
+ input.waitForCollector(new IProgressMonitor() {
+ public void beginTask(String name, int totalWork) {
+ }
+ public void done() {
+ }
+ public void internalWorked(double work) {
+ }
+ public boolean isCanceled() {
+ return false;
+ }
+ public void setCanceled(boolean value) {
+ }
+ public void setTaskName(String name) {
+ }
+ public void subTask(String name) {
+ }
+ public void worked(int work) {
+ while (Display.getCurrent().readAndDispatch()) {}
+ }
+ });
+ }
+
+ protected static void executeHeadless(CVSOperation op) throws CVSException {
+ try {
+ try {
+ // Bypass contxt by executing run(IProgressMonitor) directly
+ op.run(DEFAULT_MONITOR);
+ } catch (InvocationTargetException e1) {
+ throw CVSException.wrapException(e1);
+ }
+ } catch (InterruptedException e) {
+ throw new OperationCanceledException();
+ }
+ }
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ if (CVSTestSetup.logListener != null) {
+ try {
+ CVSTestSetup.logListener.checkErrors();
+ } catch (CoreException e) {
+ if (CVSTestSetup.FAIL_IF_EXCEPTION_LOGGED) {
+ fail("Exception written to log: ", e);
+ } else {
+ // Write the log to standard out so it can be more easily seen
+ write(e.getStatus(), 0);
+ }
+ }
+ }
+ }
+
+ protected void write(IStatus status, int indent) {
+ PrintStream output = System.out;
+ indent(output, indent);
+ output.println("Severity: " + status.getSeverity());
+
+ indent(output, indent);
+ output.println("Plugin ID: " + status.getPlugin());
+
+ indent(output, indent);
+ output.println("Code: " + status.getCode());
+
+ indent(output, indent);
+ output.println("Message: " + status.getMessage());
+
+ Throwable t = status.getException();
+ if (t != null) {
+ t.printStackTrace(output);
+ if (t instanceof CoreException) {
+ write(((CoreException)t).getStatus(), indent + 1);
+ }
+ }
+
+ if (status.isMultiStatus()) {
+ IStatus[] children = status.getChildren();
+ for (int i = 0; i < children.length; i++)
+ write(children[i], indent + 1);
+ }
+ }
+
+ protected static void indent(OutputStream output, int indent) {
+ for (int i = 0; i < indent; i++)
+ try {
+ output.write(" ".getBytes());
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#runBare()
+ */
+ public void runBare() throws Throwable {
+ try {
+ super.runBare();
+ } catch (CVSException e) {
+ // If a communication exception occurred
+ // perhaps it is a server problem
+ // Try again, just in case it is
+ if (containsCommunicationException(e)) {
+ super.runBare();
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ private boolean containsCommunicationException(CVSException e) {
+ if (e instanceof CVSCommunicationException) return true;
+ IStatus status = e.getStatus();
+ if (status.getException() instanceof CVSCommunicationException) return true;
+ if (status.isMultiStatus()) {
+ IStatus[] children = status.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IStatus child = children[i];
+ if (child.getException() instanceof CVSCommunicationException) return true;
+ }
+ }
+ return false;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.tests.harness.EclipseWorkspaceTest#ensureDoesNotExistInWorkspace(org.eclipse.core.resources.IResource)
+ */
+ public void ensureDoesNotExistInWorkspace(IResource resource) {
+ // Overridden to change how the workspace is deleted on teardown
+ if (resource.getType() == IResource.ROOT) {
+ // Delete each project individually
+ Job[] allJobs = Platform.getJobManager().find(null /* all families */);
+ IProject[] projects = ((IWorkspaceRoot)resource).getProjects();
+ try {
+ ensureDoesNotExistInWorkspace(projects);
+ } catch (AssertionFailedError e) {
+ // The delete failed. Write the active jobs to stdout
+ System.out.println("Jobs active at time of deletion failure: "); //$NON-NLS-1$
+ if (allJobs.length == 0) {
+ System.out.println("None"); //$NON-NLS-1$
+ }
+ for (int i = 0; i < allJobs.length; i++) {
+ Job job = allJobs[i];
+ System.out.println(job.getName());
+ }
+ if (CVSTestSetup.FAIL_IF_EXCEPTION_LOGGED) {
+ throw e;
+ }
+ }
+ } else {
+ super.ensureDoesNotExistInWorkspace(resource);
+ }
+ }
+
+ protected void assertStatusContainsCode(IStatus status, int code) {
+ if (status.isMultiStatus()) {
+ IStatus[] children = status.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IStatus child = children[i];
+ if (child.getCode() == code)
+ return;
+ }
+ fail("Expected status code was not present");
+ } else {
+ assertEquals("Status code is not what is expected", status.getCode(), code);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see junit.framework.TestCase#runTest()
+ */
+ protected void runTest() throws Throwable {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ Policy.recorder = new PrintStream(os);
+ try {
+ try {
+ // Override the runTest method in order to print the entire trace of a
+ // test that failed due to a CoreException including nested exceptions
+ super.runTest();
+ } catch (CoreException e) {
+ e.printStackTrace();
+ write(e.getStatus(), 0);
+ throw e;
+ }
+ } catch (Throwable e) {
+ // Transfer the recorded debug info to stdout
+ Policy.recorder.close();
+ System.out.println(new String(os.toByteArray()));
+ throw e;
+ } finally {
+ Policy.recorder.close();
+ Policy.recorder = null;
+ }
+ }
+}
+
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSChangeSetTests.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSChangeSetTests.java
new file mode 100644
index 000000000..fb513e8f2
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/CVSChangeSetTests.java
@@ -0,0 +1,419 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.tests.ccvs.core.subscriber;
+
+import java.io.ByteArrayInputStream;
+import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Test;
+
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.*;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.subscribers.*;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.internal.ccvs.core.CVSTag;
+import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin;
+import org.eclipse.team.internal.ui.synchronize.*;
+import org.eclipse.team.tests.ccvs.ui.SynchronizeViewTestAdapter;
+import org.eclipse.team.ui.synchronize.*;
+import org.eclipse.ui.PartInitException;
+
+/**
+ * Tests the change set mode of the synchronize view
+ */
+public class CVSChangeSetTests extends CVSSyncSubscriberTest {
+
+ public static Test suite() {
+ return suite(CVSChangeSetTests.class);
+ }
+
+ public CVSChangeSetTests() {
+ super();
+ }
+
+ public CVSChangeSetTests(String name) {
+ super(name);
+ }
+
+ private void assertIncomingChangesInSets(IFile[][] files, String[] messages) throws CoreException {
+ // Get the workspace subscriber which also creates a participant and page in the sync view
+ Subscriber workspaceSubscriber = getWorkspaceSubscriber();
+ refresh(workspaceSubscriber);
+ ISynchronizeModelElement root = getModelRoot(workspaceSubscriber);
+ ChangeSetDiffNode[] nodes = getCheckedInChangeSetNodes(root);
+ assertNodesInViewer(workspaceSubscriber, nodes);
+ assertEquals("The number of change sets in the sync view do not match the expected number", messages.length, nodes.length);
+ for (int i = 0; i < messages.length; i++) {
+ String message = messages[i];
+ ChangeSetDiffNode node = getCommitSetFor(root, message);
+ assertNotNull("The commit set for '" + message + "' is not in the sync view", node);
+ List filesInSet = new ArrayList();
+ getFileChildren(node, filesInSet);
+ assertTrue("The number of files in the set do not match the expected number", files[i].length == filesInSet.size());
+ for (int j = 0; j < files[i].length; j++) {
+ IFile file = files[i][j];
+ assertTrue("File " + file.getFullPath() + " is not in the set", filesInSet.contains(file));
+ }
+ }
+ }
+
+ private void assertNodesInViewer(Subscriber workspaceSubscriber, ChangeSetDiffNode[] nodes) throws PartInitException {
+ ISynchronizeParticipant participant = SynchronizeViewTestAdapter.getParticipant(workspaceSubscriber);
+ SubscriberParticipantPage page = (SubscriberParticipantPage)SynchronizeViewTestAdapter.getSyncViewPage(participant);
+ TreeViewer viewer = (TreeViewer)page.getViewer();
+ Tree tree = viewer.getTree();
+ List nodeList = new ArrayList();
+ nodeList.addAll(Arrays.asList(nodes));
+ TreeItem[] items = tree.getItems();
+ removeTreeItemsFromList(nodeList, items);
+ assertTrue("Not all nodes are visible in the view", nodeList.isEmpty());
+ }
+
+ private void removeTreeItemsFromList(List nodeList, TreeItem[] items) {
+ for (int i = 0; i < items.length; i++) {
+ TreeItem item = items[i];
+ nodeList.remove(item.getData());
+ TreeItem[] children = item.getItems();
+ removeTreeItemsFromList(nodeList, children);
+ }
+ }
+
+ private ChangeSetDiffNode[] getCheckedInChangeSetNodes(ISynchronizeModelElement root) {
+ List result = new ArrayList();
+ IDiffElement[] children = root.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IDiffElement element = children[i];
+ if (element instanceof ChangeSetDiffNode) {
+ ChangeSetDiffNode node = (ChangeSetDiffNode)element;
+ if (node.getSet() instanceof CheckedInChangeSet) {
+ result.add(node);
+ }
+ }
+ }
+ return (ChangeSetDiffNode[]) result.toArray(new ChangeSetDiffNode[result.size()]);
+ }
+
+ private ChangeSetDiffNode[] getActiveChangeSetNodes(ISynchronizeModelElement root) {
+ List result = new ArrayList();
+ IDiffElement[] children = root.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IDiffElement element = children[i];
+ if (element instanceof ChangeSetDiffNode) {
+ ChangeSetDiffNode node = (ChangeSetDiffNode)element;
+ if (node.getSet() instanceof ActiveChangeSet) {
+ result.add(node);
+ }
+ }
+ }
+ return (ChangeSetDiffNode[]) result.toArray(new ChangeSetDiffNode[result.size()]);
+ }
+
+ /**
+ * Adds IFiles to the list
+ */
+ private void getFileChildren(ISynchronizeModelElement node, List list) {
+ IResource resource = node.getResource();
+ if (resource != null && resource.getType() == IResource.FILE) {
+ list.add(resource);
+ }
+ IDiffElement[] children = node.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IDiffElement child = children[i];
+ getFileChildren((ISynchronizeModelElement)child, list);
+ }
+ return;
+ }
+
+ private ChangeSetDiffNode getCommitSetFor(ISynchronizeModelElement root, String message) {
+ IDiffElement[] children = root.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IDiffElement element = children[i];
+ if (element instanceof ChangeSetDiffNode) {
+ ChangeSetDiffNode node = (ChangeSetDiffNode)element;
+ if (node.getSet().getComment().equals(message)) {
+ return node;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void refresh(Subscriber workspaceSubscriber) throws TeamException {
+ workspaceSubscriber.refresh(workspaceSubscriber.roots(), IResource.DEPTH_INFINITE, DEFAULT_MONITOR);
+ }
+
+ private void enableChangeSets(Subscriber workspaceSubscriber) throws PartInitException {
+ ISynchronizeParticipant participant = SynchronizeViewTestAdapter.getParticipant(workspaceSubscriber);
+ SubscriberParticipantPage page = (SubscriberParticipantPage)SynchronizeViewTestAdapter.getSyncViewPage(participant);
+ ChangeSetModelManager manager = (ChangeSetModelManager)page.getConfiguration().getProperty(SynchronizePageConfiguration.P_MODEL_MANAGER);
+ manager.setCommitSetsEnabled(true);
+ page.getConfiguration().setMode(ISynchronizePageConfiguration.BOTH_MODE);
+ }
+
+ private void enableCheckedInChangeSets(Subscriber workspaceSubscriber) throws PartInitException {
+ enableChangeSets(workspaceSubscriber);
+ ISynchronizeParticipant participant = SynchronizeViewTestAdapter.getParticipant(workspaceSubscriber);
+ SubscriberParticipantPage page = (SubscriberParticipantPage)SynchronizeViewTestAdapter.getSyncViewPage(participant);
+ page.getConfiguration().setMode(ISynchronizePageConfiguration.INCOMING_MODE);
+ }
+
+ private void enableActiveChangeSets(Subscriber workspaceSubscriber) throws PartInitException {
+ enableChangeSets(workspaceSubscriber);
+ ISynchronizeParticipant participant = SynchronizeViewTestAdapter.getParticipant(workspaceSubscriber);
+ SubscriberParticipantPage page = (SubscriberParticipantPage)SynchronizeViewTestAdapter.getSyncViewPage(participant);
+ page.getConfiguration().setMode(ISynchronizePageConfiguration.OUTGOING_MODE);
+ }
+
+ /*
+ * Wait until all the background handlers have settled and then return the root element in the sync view
+ */
+ private ISynchronizeModelElement getModelRoot(Subscriber workspaceSubscriber) throws CoreException {
+ IProgressMonitor eventLoopProgressMonitor = new IProgressMonitor() {
+ public void beginTask(String name, int totalWork) {
+ }
+ public void done() {
+ }
+ public void internalWorked(double work) {
+ }
+ public boolean isCanceled() {
+ return false;
+ }
+ public void setCanceled(boolean value) {
+ }
+ public void setTaskName(String name) {
+ }
+ public void subTask(String name) {
+ }
+ public void worked(int work) {
+ while (Display.getCurrent().readAndDispatch()) {}
+ }
+ };
+ SynchronizeViewTestAdapter.getCollector(workspaceSubscriber);
+ ISynchronizeParticipant participant = SynchronizeViewTestAdapter.getParticipant(workspaceSubscriber);
+ ChangeSetCapability capability = participant.getChangeSetCapability();
+ SubscriberChangeSetCollector activeManager = capability.getActiveChangeSetManager();
+ activeManager.waitUntilDone(eventLoopProgressMonitor);
+ SubscriberParticipantPage page = (SubscriberParticipantPage)SynchronizeViewTestAdapter.getSyncViewPage(participant);
+ ChangeSetModelManager manager = (ChangeSetModelManager)page.getConfiguration().getProperty(SynchronizePageConfiguration.P_MODEL_MANAGER);
+ AbstractSynchronizeModelProvider provider = (AbstractSynchronizeModelProvider)manager.getActiveModelProvider();
+ provider.waitUntilDone(eventLoopProgressMonitor);
+ return provider.getModelRoot();
+ }
+
+ private SubscriberChangeSetCollector getActiveChangeSetManager() {
+ return CVSUIPlugin.getPlugin().getChangeSetManager();
+ }
+
+ /*
+ * Assert that the given resources make up the given set both directly
+ * and by what is displayed in the sync view.
+ */
+ private void assertInActiveSet(IResource[] resources, ActiveChangeSet set) throws CoreException {
+ assertResourcesAreTheSame(resources, set.getResources(), true);
+ ISynchronizeModelElement root = getModelRoot(getActiveChangeSetManager().getSubscriber());
+ ChangeSetDiffNode node = getChangeSetNodeFor(root, set);
+ assertNotNull("Change set " + set.getTitle() + " did not appear in the sync view", node);
+ IResource[] outOfSync = getOutOfSyncResources(node);
+ assertResourcesAreTheSame(resources, outOfSync, true);
+ // Assert that all active sets are visible in the view
+ ChangeSet[] sets = getActiveChangeSetManager().getSets();
+ for (int i = 0; i < sets.length; i++) {
+ ChangeSet changeSet = sets[i];
+ node = getChangeSetNodeFor(root, changeSet);
+ assertNotNull("The node for set " + set.getName() + " is not in the view", node);
+
+ }
+ ChangeSetDiffNode[] nodes = getActiveChangeSetNodes(root);
+ assertNodesInViewer(getWorkspaceSubscriber(), nodes);
+ }
+
+ private ChangeSetDiffNode getChangeSetNodeFor(ISynchronizeModelElement root, ChangeSet set) {
+ IDiffElement[] children = root.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IDiffElement element = children[i];
+ if (element instanceof ChangeSetDiffNode) {
+ ChangeSetDiffNode node = (ChangeSetDiffNode)element;
+ if (node.getSet() == set) {
+ return node;
+ }
+ }
+ }
+ return null;
+ }
+
+ private IResource[] getOutOfSyncResources(ISynchronizeModelElement element) {
+ ArrayList arrayList = new ArrayList();
+ getOutOfSync(element, arrayList);
+ SyncInfo[] infos = (SyncInfo[]) arrayList.toArray(new SyncInfo[arrayList.size()]);
+ IResource[] resources = getResources(infos);
+ return resources;
+ }
+
+ private IResource[] getResources(SyncInfo[] infos) {
+ IResource[] resources = new IResource[infos.length];
+ for (int i = 0; i < resources.length; i++) {
+ resources[i] = infos[i].getLocal();
+ }
+ return resources;
+ }
+
+ private void getOutOfSync(ISynchronizeModelElement node, List list) {
+ SyncInfo info = getSyncInfo(node);
+ if (info != null && info.getKind() != SyncInfo.IN_SYNC) {
+ list.add(info);
+ }
+ IDiffElement[] children = node.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IDiffElement child = children[i];
+ getOutOfSync((ISynchronizeModelElement)child, list);
+ }
+ return;
+ }
+
+ private SyncInfo getSyncInfo(ISynchronizeModelElement node) {
+ if (node instanceof IAdaptable) {
+ return (SyncInfo)((IAdaptable)node).getAdapter(SyncInfo.class);
+ }
+ return null;
+ }
+
+ private void assertResourcesAreTheSame(IResource[] resources1, IResource[] resources2, boolean doNotAllowExtra) {
+ if (doNotAllowExtra) {
+ if (resources1.length != resources2.length) {
+ System.out.println("Expected");
+ for (int i = 0; i < resources1.length; i++) {
+ IResource resource = resources1[i];
+ System.out.println(resource.getFullPath().toString());
+ }
+ System.out.println("Actual");
+ for (int i = 0; i < resources2.length; i++) {
+ IResource resource = resources2[i];
+ System.out.println(resource.getFullPath().toString());
+ }
+ }
+ assertEquals("The number of resources do not match the expected number", resources1.length, resources2.length);
+ }
+ for (int i = 0; i < resources1.length; i++) {
+ IResource resource = resources1[i];
+ boolean found = false;
+ for (int j = 0; j < resources2.length; j++) {
+ IResource resource2 = resources2[j];
+ if (resource2.equals(resource)) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue("Expected resource " + resource.getFullPath().toString() + " was not present", found);
+ }
+ }
+
+ /*
+ * Assert that the given resources make up the root set
+ * displayed in the sync view. The root set is those
+ * resources that are not part of an active change set.
+ */
+ private void assertInRootSet(IResource[] resources) throws CoreException {
+ ISynchronizeModelElement[] nodes = getNonChangeSetRoots(getModelRoot(getActiveChangeSetManager().getSubscriber()));
+ List list = new ArrayList();
+ for (int i = 0; i < nodes.length; i++) {
+ ISynchronizeModelElement element = nodes[i];
+ getOutOfSync(element, list);
+ }
+ IResource[] outOfSync = getResources((SyncInfo[]) list.toArray(new SyncInfo[list.size()]));
+ // Only require that the expected resources are there but allow extra.
+ // This is required because of junk left over from previous tests.
+ // This means there is a bug somewhere. But where?
+ assertResourcesAreTheSame(resources, outOfSync, false /* allow extra out-of-sync resources */);
+
+ }
+
+ private ISynchronizeModelElement[] getNonChangeSetRoots(ISynchronizeModelElement modelRoot) {
+ List result = new ArrayList();
+ IDiffElement[] children = modelRoot.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ IDiffElement element = children[i];
+ if (!(element instanceof ChangeSetDiffNode)) {
+ result.add(element);
+ }
+ }
+ return (ISynchronizeModelElement[]) result.toArray(new ISynchronizeModelElement[result.size()]);
+ }
+
+ public void testSimpleCommit() throws CoreException {
+ enableCheckedInChangeSets(getWorkspaceSubscriber());
+
+ IProject project = createProject(new String[] { "file1.txt", "file2.txt", "folder1/", "folder1/a.txt", "folder1/b.txt"});
+
+ // Modify a file in a copy
+ IProject copy = checkoutCopy(project, CVSTag.DEFAULT);
+ setContentsAndEnsureModified(copy.getFile("file1.txt"));
+ String message1 = "Commit 1";
+ commitResources(new IResource[] {copy}, IResource.DEPTH_INFINITE, message1);
+ assertIncomingChangesInSets(new IFile[][] {{ project.getFile("file1.txt") }}, new String[] {message1});
+
+ // Modify the copy some more
+ setContentsAndEnsureModified(copy.getFile("file2.txt"));
+ setContentsAndEnsureModified(copy.getFile("folder1/a.txt"));
+ String message2 = "Commit 2";
+ commitResources(new IResource[] {copy}, IResource.DEPTH_INFINITE, message2);
+ assertIncomingChangesInSets(new IFile[][] {
+ { project.getFile("file1.txt") },
+ { project.getFile("file2.txt"), project.getFile("folder1/a.txt") }
+ }, new String[] {message1, message2});
+
+ // Modify the copy some more
+ setContentsAndEnsureModified(copy.getFile("file2.txt"));
+ String message3 = "Commit 3";
+ commitResources(new IResource[] {copy}, IResource.DEPTH_INFINITE, message3);
+ assertIncomingChangesInSets(new IFile[][] {
+ { project.getFile("file1.txt") },
+ { project.getFile("folder1/a.txt") },
+ { project.getFile("file2.txt")}
+ }, new String[] {message1, message2, message3});
+
+ // Now commit the files in one of the sets and ensure it is removed from the view
+ updateResources(new IResource[] { project.getFile("file1.txt")}, false);
+ assertIncomingChangesInSets(new IFile[][] {
+ { project.getFile("folder1/a.txt") },
+ { project.getFile("file2.txt")}
+ }, new String[] {message2, message3});
+ }
+
+ public void testSimpleActiveChangeSet() throws CoreException {
+ IProject project = createProject(new String[] { "file1.txt", "file2.txt", "folder1/", "folder1/a.txt", "folder1/b.txt"});
+ // Enable Change Sets
+ enableActiveChangeSets(getWorkspaceSubscriber());
+ // Add a folder and file
+ IFolder newFolder = project.getFolder("folder2");
+ newFolder.create(false, true, null);
+ IFile newFile = newFolder.getFile("file.txt");
+ newFile.create(new ByteArrayInputStream("Hi There".getBytes()), false, null);
+ // Create an active commit set and assert that it appears in the sync view
+ SubscriberChangeSetCollector manager = getActiveChangeSetManager();
+ ActiveChangeSet set = manager.createSet("test", new SyncInfo[0]);
+ manager.add(set);
+ assertInActiveSet(new IResource[] { }, set);
+ assertInRootSet(new IResource[] {newFolder, newFile});
+ // Add the new file to the set and assert that the file is in the set and the folder is still at the root
+ set.add(new IResource[] { newFile });
+ assertInActiveSet(new IResource[] { newFile }, set);
+ assertInRootSet(new IResource[] {newFolder });
+ // Add the folder to the set
+ set.add(new IResource[] { newFolder });
+ assertInActiveSet(new IResource[] { newFolder, newFile }, set);
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/SyncInfoSource.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/SyncInfoSource.java
new file mode 100644
index 000000000..1766aa9ae
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/core/subscriber/SyncInfoSource.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.tests.ccvs.core.subscriber;
+
+import java.util.*;
+
+import junit.framework.AssertionFailedError;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.subscribers.Subscriber;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.internal.ccvs.core.*;
+
+/**
+ * This class acts as the source for the sync info used by the subscriber tests.
+ * The purpose is to allow the sync info to be obtained directly from the subscriber
+ * or through the sync set visible in the sync view.
+ */
+public class SyncInfoSource {
+
+ protected static IProgressMonitor DEFAULT_MONITOR = new NullProgressMonitor();
+ protected List mergeSubscribers = new ArrayList();
+ protected List compareSubscribers = new ArrayList();
+
+ public CVSMergeSubscriber createMergeSubscriber(IProject project, CVSTag root, CVSTag branch) {
+ CVSMergeSubscriber subscriber = new CVSMergeSubscriber(new IResource[] { project }, root, branch);
+ mergeSubscribers.add(subscriber);
+ return subscriber;
+ }
+
+ public CVSCompareSubscriber createCompareSubscriber(IResource resource, CVSTag tag) {
+ CVSCompareSubscriber subscriber = new CVSCompareSubscriber(new IResource[] { resource }, tag);
+ compareSubscribers.add(subscriber);
+ return subscriber;
+ }
+
+ public Subscriber createWorkspaceSubscriber() throws TeamException {
+ return CVSProviderPlugin.getPlugin().getCVSWorkspaceSubscriber();
+ }
+
+ /**
+ * Return the sync info for the given subscriber for the given resource.
+ */
+ public SyncInfo getSyncInfo(Subscriber subscriber, IResource resource) throws TeamException {
+ return subscriber.getSyncInfo(resource);
+ }
+
+ /**
+ * Refresh the subscriber for the given resource
+ */
+ public void refresh(Subscriber subscriber, IResource resource) throws TeamException {
+ refresh(subscriber, new IResource[] { resource});
+ }
+
+ /**
+ * Refresh the subscriber for the given resources
+ */
+ public void refresh(Subscriber subscriber, IResource[] resources) throws TeamException {
+ subscriber.refresh(resources, IResource.DEPTH_INFINITE, DEFAULT_MONITOR);
+ }
+
+ protected void assertProjectRemoved(Subscriber subscriber, IProject project) throws TeamException {
+ IResource[] roots = subscriber.roots();
+ for (int i = 0; i < roots.length; i++) {
+ IResource resource = roots[i];
+ if (resource.equals(project)) {
+ throw new AssertionFailedError();
+ }
+ }
+ }
+
+ public void tearDown() {
+ for (Iterator it = mergeSubscribers.iterator(); it.hasNext(); ) {
+ CVSMergeSubscriber s = (CVSMergeSubscriber) it.next();
+ s.cancel();
+ }
+ }
+
+ /**
+ * Recalculate a sync info from scratch
+ */
+ public void reset(Subscriber subscriber) throws TeamException {
+ // Do nothing
+
+ }
+
+ /**
+ * Assert that the model for the subscriber matches what is being displayed.
+ * Default is to do nothing. Subclasses may override
+ * @param subscriber the subscriber
+ */
+ public void assertViewMatchesModel(Subscriber subscriber) {
+ // Default is to do nothing. Subclasses may override
+ }
+}
diff --git a/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/SynchronizeViewTestAdapter.java b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/SynchronizeViewTestAdapter.java
new file mode 100644
index 000000000..416e4d1b3
--- /dev/null
+++ b/tests/org.eclipse.team.tests.cvs.core/src/org/eclipse/team/tests/ccvs/ui/SynchronizeViewTestAdapter.java
@@ -0,0 +1,338 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.tests.ccvs.ui;
+
+import junit.framework.AssertionFailedError;
+
+import org.eclipse.compare.structuremergeviewer.IDiffContainer;
+import org.eclipse.compare.structuremergeviewer.IDiffElement;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.swt.widgets.*;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.subscribers.Subscriber;
+import org.eclipse.team.core.synchronize.*;
+import org.eclipse.team.internal.ccvs.core.*;
+import org.eclipse.team.internal.ccvs.ui.subscriber.*;
+import org.eclipse.team.internal.core.subscribers.SubscriberSyncInfoCollector;
+import org.eclipse.team.internal.ui.TeamUIPlugin;
+import org.eclipse.team.internal.ui.synchronize.*;
+import org.eclipse.team.tests.ccvs.core.EclipseTest;
+import org.eclipse.team.tests.ccvs.core.subscriber.SyncInfoSource;
+import org.eclipse.team.ui.TeamUI;
+import org.eclipse.team.ui.synchronize.*;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.part.IPage;
+
+/**
+ * SyncInfoSource that obtains SyncInfo from the SynchronizeView's SyncSet.
+ */
+public class SynchronizeViewTestAdapter extends SyncInfoSource {
+
+ public SynchronizeViewTestAdapter() {
+ IWorkbenchPage activePage = TeamUIPlugin.getActivePage();
+ try {
+ activePage.showView(ISynchronizeView.VIEW_ID);
+ } catch (PartInitException e) {
+ throw new AssertionFailedError("Cannot show sync view in active page");
+ }
+ }
+
+ public SyncInfo getSyncInfo(Subscriber subscriber, IResource resource) throws TeamException {
+ // Wait for the collector
+ SyncInfoSet set = getCollector(subscriber).getSyncInfoSet();
+ // Obtain the sync info from the viewer to ensure that the
+ // entire chain has the proper state
+ SyncInfo info = internalGetSyncInfo(subscriber, resource);
+ // Do a sanity check on the collected sync info
+ if (info == null) {
+ info = subscriber.getSyncInfo(resource);
+ if ((info != null && info.getKind() != SyncInfo.IN_SYNC)) {
+ throw new AssertionFailedError(
+ "Sync state for "
+ + resource.getFullPath()
+ + " was "
+ + SyncInfo.kindToString(info.getKind())
+ + " but resource was not collected");
+ }
+ } else {
+ SyncInfo realInfo = subscriber.getSyncInfo(resource);
+ if (info.getKind() != realInfo.getKind()) {
+ throw new AssertionFailedError(
+ "Collected sync state for "
+ + resource.getFullPath()
+ + " was "
+ + SyncInfo.kindToString(info.getKind())
+ + " but the real state was "
+ + SyncInfo.kindToString(realInfo.getKind()));
+ }
+ }
+ return info;
+ }
+
+ public static SubscriberParticipant getParticipant(Subscriber subscriber) {
+ // show the sync view
+ ISynchronizeParticipantReference[] participants = TeamUI.getSynchronizeManager().getSynchronizeParticipants();
+ for (int i = 0; i < participants.length; i++) {
+ ISynchronizeParticipant participant;
+ try {
+ participant = participants[i].getParticipant();
+ } catch (TeamException e) {
+ return null;
+ }
+ if(participant instanceof SubscriberParticipant) {
+ if(((SubscriberParticipant)participant).getSubscriber() == subscriber) {
+ return (SubscriberParticipant)participant;
+ }
+ }
+ }
+ return null;
+ }
+
+ public static SubscriberSyncInfoCollector getCollector(Subscriber subscriber) {
+ SubscriberParticipant participant = getParticipant(subscriber);
+ if (participant == null) return null;
+ SubscriberSyncInfoCollector syncInfoCollector = participant.getSubscriberSyncInfoCollector();
+ EclipseTest.waitForSubscriberInputHandling(syncInfoCollector);
+ SubscriberParticipantPage page = getPage(subscriber);
+ SynchronizeModelManager manager = (SynchronizeModelManager)page.getConfiguration().getProperty(SynchronizePageConfiguration.P_MODEL_MANAGER);
+ AbstractSynchronizeModelProvider provider = (AbstractSynchronizeModelProvider)manager.getActiveModelProvider();
+ provider.waitUntilDone(new IProgressMonitor() {
+ public void beginTask(String name, int totalWork) {
+ }
+ public void done() {
+ }
+ public void internalWorked(double work) {
+ }
+ public boolean isCanceled() {
+ return false;
+ }
+ public void setCanceled(boolean value) {
+ }
+ public void setTaskName(String name) {
+ }
+ public void subTask(String name) {
+ }
+ public void worked(int work) {
+ while (Display.getCurrent().readAndDispatch()) {}
+ }
+ });
+ return syncInfoCollector;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.tests.ccvs.core.subscriber.SyncInfoSource#assertProjectRemoved(org.eclipse.team.core.subscribers.TeamSubscriber, org.eclipse.core.resources.IProject)
+ */
+ protected void assertProjectRemoved(Subscriber subscriber, IProject project) throws TeamException {
+ super.assertProjectRemoved(subscriber, project);
+ SyncInfoTree set = getCollector(subscriber).getSyncInfoSet();
+ if (set.hasMembers(project)) {
+ throw new AssertionFailedError("The sync set still contains resources from the deleted project " + project.getName());
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.tests.ccvs.core.subscriber.SyncInfoSource#createMergeSubscriber(org.eclipse.core.resources.IProject, org.eclipse.team.internal.ccvs.core.CVSTag, org.eclipse.team.internal.ccvs.core.CVSTag)
+ */
+ public CVSMergeSubscriber createMergeSubscriber(IProject project, CVSTag root, CVSTag branch) {
+ CVSMergeSubscriber mergeSubscriber = super.createMergeSubscriber(project, root, branch);
+ ISynchronizeManager synchronizeManager = TeamUI.getSynchronizeManager();
+ SubscriberParticipant participant = new MergeSynchronizeParticipant(mergeSubscriber);
+ synchronizeManager.addSynchronizeParticipants(
+ new ISynchronizeParticipant[] {participant});
+ IWorkbenchPage activePage = TeamUIPlugin.getActivePage();
+ try {
+ ISynchronizeView view = (ISynchronizeView)activePage.showView(ISynchronizeView.VIEW_ID);
+ view.display(participant);
+ } catch (PartInitException e) {
+ throw new AssertionFailedError("Cannot show sync view in active page");
+ }
+ return mergeSubscriber;
+ }
+
+ public Subscriber createWorkspaceSubscriber() throws TeamException {
+ ISynchronizeManager synchronizeManager = TeamUI.getSynchronizeManager();
+ ISynchronizeParticipantReference[] participants = synchronizeManager.get(WorkspaceSynchronizeParticipant.ID);
+ if (participants.length > 0) {
+ return ((SubscriberParticipant)participants[0].getParticipant()).getSubscriber();
+ }
+ SubscriberParticipant participant = new WorkspaceSynchronizeParticipant(new WorkspaceScope());
+ synchronizeManager.addSynchronizeParticipants(
+ new ISynchronizeParticipant[] {participant});
+ IWorkbenchPage activePage = TeamUIPlugin.getActivePage();
+ try {
+ ISynchronizeView view = (ISynchronizeView)activePage.showView(ISynchronizeView.VIEW_ID);
+ view.display(participant);
+ } catch (PartInitException e) {
+ throw new AssertionFailedError("Cannot show sync view in active page");
+ }
+ return participant.getSubscriber();
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.tests.ccvs.core.subscriber.SyncInfoSource#createCompareSubscriber(org.eclipse.core.resources.IProject, org.eclipse.team.internal.ccvs.core.CVSTag)
+ */
+ public CVSCompareSubscriber createCompareSubscriber(IResource resource, CVSTag tag) {
+ CVSCompareSubscriber s = super.createCompareSubscriber(resource, tag);
+ ISynchronizeManager synchronizeManager = TeamUI.getSynchronizeManager();
+ SubscriberParticipant participant = new CompareParticipant(s);
+ synchronizeManager.addSynchronizeParticipants(
+ new ISynchronizeParticipant[] {participant});
+ IWorkbenchPage activePage = TeamUIPlugin.getActivePage();
+ try {
+ ISynchronizeView view = (ISynchronizeView)activePage.showView(ISynchronizeView.VIEW_ID);
+ view.display(participant);
+ } catch (PartInitException e) {
+ throw new AssertionFailedError("Cannot show sync view in active page");
+ }
+ return s;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.tests.ccvs.core.subscriber.SyncInfoSource#tearDown()
+ */
+ public void tearDown() {
+ ISynchronizeParticipantReference[] participants = TeamUI.getSynchronizeManager().getSynchronizeParticipants();
+ for (int i = 0; i < participants.length; i++) {
+ try {
+ ISynchronizeParticipantReference ref = participants[i];
+ if(ref.getParticipant().getId().equals(CVSMergeSubscriber.ID)) {
+ TeamUI.getSynchronizeManager().removeSynchronizeParticipants(new ISynchronizeParticipant[] {ref.getParticipant()});
+ }
+ } catch (TeamException e) {
+ return;
+ }
+ }
+ // Process all async events that may have been generated above
+ while (Display.getCurrent().readAndDispatch()) {};
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.tests.ccvs.core.subscriber.SyncInfoSource#refresh(org.eclipse.team.core.subscribers.Subscriber, org.eclipse.core.resources.IResource[])
+ */
+ public void refresh(Subscriber subscriber, IResource[] resources)
+ throws TeamException {
+ super.refresh(subscriber, resources);
+ // Getting the collector waits for the subscriber input handlers
+ getCollector(subscriber);
+ assertViewMatchesModel(subscriber);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.team.tests.ccvs.core.subscriber.SyncInfoSource#reset()
+ */
+ public void reset(Subscriber subscriber) throws TeamException {
+ super.reset(subscriber);
+ getCollector(subscriber).reset();
+ }
+
+ private SyncInfo internalGetSyncInfo(Subscriber subscriber, IResource resource) {
+ ISynchronizeModelElement root = getModelRoot(subscriber);
+ return findSyncInfo(root, resource);
+ }
+
+ private SyncInfo findSyncInfo(ISynchronizeModelElement node, IResource resource) {
+ if (node instanceof SyncInfoModelElement) {
+ SyncInfoModelElement element = (SyncInfoModelElement)node;
+ if (element.getResource().equals(resource)) {
+ return element.getSyncInfo();
+ }
+ }
+ IDiffElement[] children = node.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ ISynchronizeModelElement child = (ISynchronizeModelElement)children[i];
+ SyncInfo info = findSyncInfo(child, resource);
+ if (info != null)
+ return info;
+ }
+ return null;
+ }
+
+ public static ISynchronizePage getSyncViewPage(ISynchronizeParticipant participant) throws PartInitException {
+ IWorkbenchPage activePage = TeamUIPlugin.getActivePage();
+ ISynchronizeView view = (ISynchronizeView)activePage.showView(ISynchronizeView.VIEW_ID);
+ IPage page = ((SynchronizeView)view).getPage(participant);
+ return (ISynchronizePage)page;
+ }
+
+ public void assertViewMatchesModel(Subscriber subscriber) {
+ ISynchronizeModelElement root = getModelRoot(subscriber);
+ TreeItem[] rootItems = getTreeItems(subscriber);
+ assertMatchingTrees(root, rootItems, root.getChildren());
+ }
+
+ private ISynchronizeModelElement getModelRoot(Subscriber subscriber) {
+ SubscriberParticipantPage page = getPage(subscriber);
+ return page.getViewerAdvisor().getModelManager().getModelRoot();
+ }
+
+ private TreeItem[] getTreeItems(Subscriber subscriber) {
+ SubscriberParticipantPage page = getPage(subscriber);
+ Viewer v = page.getViewer();
+ if (v instanceof TreeViewer) {
+ TreeViewer treeViewer = (TreeViewer)v;
+ treeViewer.expandAll();
+ Tree t = (treeViewer).getTree();
+ return t.getItems();
+ }
+ throw new AssertionFailedError("The tree for " + subscriber.getName() + " could not be retrieved");
+ }
+
+ private static SubscriberParticipantPage getPage(Subscriber subscriber) {
+ try {
+ SubscriberParticipant participant = getParticipant(subscriber);
+ IWorkbenchPage activePage = TeamUIPlugin.getActivePage();
+ ISynchronizeView view = (ISynchronizeView)activePage.showView(ISynchronizeView.VIEW_ID);
+ IPage page = ((SynchronizeView)view).getPage(participant);
+ if (page instanceof SubscriberParticipantPage) {
+ SubscriberParticipantPage subscriberPage = (SubscriberParticipantPage)page;
+ return subscriberPage;
+ }
+ } catch (PartInitException e) {
+ throw new AssertionFailedError("Cannot show sync view in active page");
+ }
+ throw new AssertionFailedError("The page for " + subscriber.getName() + " could not be retrieved");
+ }
+
+ private void assertMatchingTrees(IDiffElement parent, TreeItem[] items, IDiffElement[] children) {
+ if ((items == null || items.length == 0) && (children == null || children.length == 0)) {
+ // No childen in either case so just return
+ return;
+ }
+ if (items == null || children == null || items.length != children.length) {
+ throw new AssertionFailedError("The number of children of " + parent.getName() + " is " +
+ (children == null ? 0: children.length) + " but the view has " +
+ (items == null ? 0 : items.length));
+ }
+ for (int i = 0; i < children.length; i++) {
+ IDiffElement element = children[i];
+ TreeItem foundItem = null;
+ for (int j = 0; j < items.length; j++) {
+ TreeItem item = items[j];
+ if (item.getData() == element) {
+ foundItem = item;
+ break;
+ }
+ }
+ if (foundItem == null) {
+ throw new AssertionFailedError("Element" + element.getName() + " is in the model but not in the view");
+ } else {
+ assertMatchingTrees(element, foundItem.getItems(), ((IDiffContainer)element).getChildren());
+ }
+ }
+
+ }
+}

Back to the top