diff options
author | cvs2svn | 2004-12-02 12:31:50 +0000 |
---|---|---|
committer | cvs2svn | 2004-12-02 12:31:50 +0000 |
commit | 4abda86bad64803c35f06903d34108db63f05739 (patch) | |
tree | db57d36b53e75a6dc885c0420d08063f3018cf2a | |
parent | bce7ada93c6744ce6d44fb5619cae03190a61eb5 (diff) | |
download | eclipse.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
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 <samp>IConnectionMethod</samp> + </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: + +<p> +<pre> + <extension id="pserver" point="org.eclipse.team.cvs.core.connectionmethods"> + <adapter> + <run class="org.eclipse.team.internal.ccvs.core.connection.PServerConnectionMethod"> + <parameter name="trace" value="false"> + </parameter> + </run> + </adapter> + </extension> +</pre> +</p> + </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 <a href="http://www.eclipse.org/legal/cpl-v10.html">http://www.eclipse.org/legal/cpl-v10.html</a> + </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()); + } + } + + } +} |