KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > core > CVSTeamProvider


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.team.internal.ccvs.core;
12  
13 import java.io.*;
14 import java.net.URI JavaDoc;
15 import java.util.*;
16
17 import org.eclipse.core.resources.*;
18 import org.eclipse.core.resources.team.*;
19 import org.eclipse.core.runtime.*;
20 import org.eclipse.core.runtime.Status;
21 import org.eclipse.core.runtime.jobs.ISchedulingRule;
22 import org.eclipse.osgi.util.NLS;
23 import org.eclipse.team.core.RepositoryProvider;
24 import org.eclipse.team.core.TeamException;
25 import org.eclipse.team.core.history.IFileHistoryProvider;
26 import org.eclipse.team.internal.ccvs.core.client.*;
27 import org.eclipse.team.internal.ccvs.core.client.Command.KSubstOption;
28 import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
29 import org.eclipse.team.internal.ccvs.core.client.listeners.*;
30 import org.eclipse.team.internal.ccvs.core.filehistory.CVSFileHistoryProvider;
31 import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot;
32 import org.eclipse.team.internal.ccvs.core.resources.EclipseSynchronizer;
33 import org.eclipse.team.internal.ccvs.core.syncinfo.*;
34 import org.eclipse.team.internal.ccvs.core.util.*;
35 import org.eclipse.team.internal.core.streams.CRLFtoLFInputStream;
36 import org.eclipse.team.internal.core.streams.LFtoCRLFInputStream;
37
38 /**
39  * CVS implementation of {@link RepositoryProvider}
40  */

41 public class CVSTeamProvider extends RepositoryProvider {
42
43     private static final ResourceRuleFactory RESOURCE_RULE_FACTORY = new CVSResourceRuleFactory();
44     
45     private static final boolean IS_CRLF_PLATFORM = Arrays.equals(
46         System.getProperty("line.separator").getBytes(), new byte[] { '\r', '\n' }); //$NON-NLS-1$
47

48     public static final IStatus OK = new Status(IStatus.OK, CVSProviderPlugin.ID, 0, CVSMessages.ok, null);
49     
50     private CVSWorkspaceRoot workspaceRoot;
51     private IProject project;
52     
53     private static MoveDeleteHook moveDeleteHook= new MoveDeleteHook();
54     private static CVSCoreFileModificationValidator fileModificationValidator;
55     private static CVSFileHistoryProvider fileHistoryProvider;
56     
57     // property used to indicate whether new directories should be discovered for the project
58
private final static QualifiedName FETCH_ABSENT_DIRECTORIES_PROP_KEY =
59         new QualifiedName("org.eclipse.team.cvs.core", "fetch_absent_directories"); //$NON-NLS-1$ //$NON-NLS-2$
60
// property used to indicate whether the project is configured to use Watch/edit
61
private final static QualifiedName WATCH_EDIT_PROP_KEY =
62         new QualifiedName("org.eclipse.team.cvs.core", "watch_edit"); //$NON-NLS-1$ //$NON-NLS-2$
63

64     /**
65      * Session property key used to indicate that the project, although not officially shared,
66      * is a target of a CVS operation.
67      */

68     private static final QualifiedName TEMP_SHARED = new QualifiedName(CVSProviderPlugin.ID, "tempShare"); //$NON-NLS-1$
69

70     /**
71      * Return whether the project is mapped to CVS or is the target of a CVS operation
72      * that will most likely lead to the project being shared.
73      * @param project the project
74      * @return whether the project is mapped to CVS or is the target of a CVS operation
75      * that will most likely lead to the project being shared
76      */

77     public static boolean isSharedWithCVS(IProject project) {
78         if (project.isAccessible()) {
79             if (RepositoryProvider.isShared(project)) {
80                 RepositoryProvider provider = RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId());
81                 if (provider != null)
82                     return true;
83             }
84             try {
85                 Object JavaDoc sessionProperty = project.getSessionProperty(TEMP_SHARED);
86                 return sessionProperty != null && sessionProperty.equals(Boolean.TRUE);
87             } catch (CoreException e) {
88                 CVSProviderPlugin.log(e);
89             }
90         }
91         return false;
92     }
93     
94     /**
95      * Mark the project as being a target of a CVS operation so the sync info management
96      * will occur.
97      * @param project the project
98      */

99     public static void markAsTempShare(IProject project) {
100         if (RepositoryProvider.isShared(project))
101             return;
102         try {
103             project.setSessionProperty(CVSTeamProvider.TEMP_SHARED, Boolean.TRUE);
104         } catch (CoreException e) {
105             CVSProviderPlugin.log(e);
106         }
107     }
108     
109     /**
110      * Return the file modification validator used for all CVS repository providers.
111      * @return the file modification validator used for all CVS repository providers
112      */

113     protected static CVSCoreFileModificationValidator internalGetFileModificationValidator() {
114         if (CVSTeamProvider.fileModificationValidator == null) {
115             CVSTeamProvider.fileModificationValidator = new CVSCoreFileModificationValidator();
116         }
117         return CVSTeamProvider.fileModificationValidator;
118     }
119     
120     /**
121      * No-arg Constructor for IProjectNature conformance
122      */

123     public CVSTeamProvider() {
124     }
125
126     /* (non-Javadoc)
127      * @see org.eclipse.core.resources.IProjectNature#deconfigure()
128      */

129     public void deconfigure() {
130     }
131     
132     /* (non-Javadoc)
133      * @see org.eclipse.team.core.RepositoryProvider#deconfigured()
134      */

135     public void deconfigured() {
136         // when a nature is removed from the project, notify the synchronizer that
137
// we no longer need the sync info cached. This does not affect the actual CVS
138
// meta directories on disk, and will remain unless a client calls unmanage().
139
try {
140             EclipseSynchronizer.getInstance().deconfigure(getProject(), null);
141             internalSetWatchEditEnabled(null);
142             internalSetFetchAbsentDirectories(null);
143         } catch(CVSException e) {
144             // Log the exception and let the disconnect continue
145
CVSProviderPlugin.log(e);
146         }
147         ResourceStateChangeListeners.getListener().projectDeconfigured(getProject());
148     }
149     /**
150      * @see IProjectNature#getProject()
151      */

152     public IProject getProject() {
153         return project;
154     }
155
156     /**
157      * @see IProjectNature#setProject(IProject)
158      */

159     public void setProject(IProject project) {
160         this.project = project;
161         this.workspaceRoot = new CVSWorkspaceRoot(project);
162         // We used to check to see if the project had CVS folders and log
163
// if it didn't However, in some scenarios, the project can be mapped
164
// before the CVS folders have been created (see bug 173610)
165
}
166
167     /**
168      * Return the remote location to which the receiver's project is mapped.
169      */

170     public ICVSRepositoryLocation getRemoteLocation() throws CVSException {
171         try {
172             return workspaceRoot.getRemoteLocation();
173         } catch (CVSException e) {
174             // If we can't get the remote location, we should disconnect since nothing can be done with the provider
175
try {
176                 RepositoryProvider.unmap(project);
177             } catch (TeamException ex) {
178                 CVSProviderPlugin.log(ex);
179             }
180             // We need to trigger a decorator refresh
181
throw e;
182         }
183     }
184     
185     public CVSWorkspaceRoot getCVSWorkspaceRoot() {
186         return workspaceRoot;
187     }
188     
189     /*
190      * Generate an exception if the resource is not a child of the project
191      */

192      private void checkIsChild(IResource resource) throws CVSException {
193         if (!isChildResource(resource))
194             throw new CVSException(new Status(IStatus.ERROR, CVSProviderPlugin.ID, TeamException.UNABLE,
195                 NLS.bind(CVSMessages.CVSTeamProvider_invalidResource, (new Object JavaDoc[] {resource.getFullPath().toString(), project.getName()})),
196                 null));
197      }
198      
199     /*
200      * Get the arguments to be passed to a commit or update
201      */

202     private String JavaDoc[] getValidArguments(IResource[] resources, LocalOption[] options) throws CVSException {
203         List arguments = new ArrayList(resources.length);
204         for (int i=0;i<resources.length;i++) {
205             checkIsChild(resources[i]);
206             IPath cvsPath = resources[i].getFullPath().removeFirstSegments(1);
207             if (cvsPath.segmentCount() == 0) {
208                 arguments.add(Session.CURRENT_LOCAL_FOLDER);
209             } else {
210                 arguments.add(cvsPath.toString());
211             }
212         }
213         return (String JavaDoc[])arguments.toArray(new String JavaDoc[arguments.size()]);
214     }
215     
216     private ICVSResource[] getCVSArguments(IResource[] resources) {
217         ICVSResource[] cvsResources = new ICVSResource[resources.length];
218         for (int i = 0; i < cvsResources.length; i++) {
219             cvsResources[i] = CVSWorkspaceRoot.getCVSResourceFor(resources[i]);
220         }
221         return cvsResources;
222     }
223     
224     /*
225      * This method expects to be passed an InfiniteSubProgressMonitor
226      */

227     public void setRemoteRoot(ICVSRepositoryLocation location, IProgressMonitor monitor) throws TeamException {
228
229         // Check if there is a differnece between the new and old roots
230
final String JavaDoc root = location.getLocation(false);
231         if (root.equals(workspaceRoot.getRemoteLocation()))
232             return;
233     
234         try {
235             workspaceRoot.getLocalRoot().run(new ICVSRunnable() {
236                 public void run(IProgressMonitor progress) throws CVSException {
237                     try {
238                         // 256 ticks gives us a maximum of 1024 which seems reasonable for folders is a project
239
progress.beginTask(null, 100);
240                         final IProgressMonitor monitor = Policy.infiniteSubMonitorFor(progress, 100);
241                         monitor.beginTask(null, 256);
242         
243                         // Visit all the children folders in order to set the root in the folder sync info
244
workspaceRoot.getLocalRoot().accept(new ICVSResourceVisitor() {
245                             public void visitFile(ICVSFile file) throws CVSException {}
246                             public void visitFolder(ICVSFolder folder) throws CVSException {
247                                 monitor.worked(1);
248                                 FolderSyncInfo info = folder.getFolderSyncInfo();
249                                 if (info != null) {
250                                     monitor.subTask(NLS.bind(CVSMessages.CVSTeamProvider_updatingFolder, new String JavaDoc[] { info.getRepository() }));
251                                     MutableFolderSyncInfo newInfo = info.cloneMutable();
252                                     newInfo.setRoot(root);
253                                     folder.setFolderSyncInfo(newInfo);
254                                     folder.acceptChildren(this);
255                                 }
256                             }
257                         });
258                     } finally {
259                         progress.done();
260                     }
261                 }
262             }, monitor);
263         } finally {
264             monitor.done();
265         }
266     }
267     
268     /*
269      * Helper to indicate if the resource is a child of the receiver's project
270      */

271     private boolean isChildResource(IResource resource) {
272         return resource.getProject().getName().equals(project.getName());
273     }
274     
275     public void configureProject() throws CoreException {
276         getProject().setSessionProperty(TEMP_SHARED, null);
277         ResourceStateChangeListeners.getListener().projectConfigured(getProject());
278     }
279     /**
280      * Sets the keyword substitution mode for the specified resources.
281      * <p>
282      * Applies the following rules in order:<br>
283      * <ul>
284      * <li>If a file is not managed, skips it.</li>
285      * <li>If a file is not changing modes, skips it.</li>
286      * <li>If a file is being changed from binary to text, corrects line delimiters
287      * then commits it, then admins it.</li>
288      * <li>If a file is added, changes the resource sync information locally.</li>
289      * <li>Otherwise commits the file (with FORCE to create a new revision), then admins it.</li>
290      * </ul>
291      * All files that are admin'd are committed with FORCE to prevent other developers from
292      * casually trying to commit pending changes to the repository without first checking out
293      * a new copy. This is not a perfect solution, as they could just as easily do an UPDATE
294      * and not obtain the new keyword sync info.
295      * </p>
296      *
297      * @param changeSet a map from IFile to KSubstOption
298      * @param monitor the progress monitor
299      * @return a status code indicating success or failure of the operation
300      *
301      * @throws TeamException
302      */

303     public IStatus setKeywordSubstitution(final Map /* from IFile to KSubstOption */ changeSet,
304         final String JavaDoc comment,
305         IProgressMonitor monitor) throws TeamException {
306         final IStatus[] result = new IStatus[] { ICommandOutputListener.OK };
307         workspaceRoot.getLocalRoot().run(new ICVSRunnable() {
308             public void run(final IProgressMonitor monitor) throws CVSException {
309                 final Map /* from KSubstOption to List of String */ filesToAdmin = new HashMap();
310                 final Collection /* of ICVSFile */ filesToCommitAsText = new HashSet(); // need fast lookup
311
final boolean useCRLF = IS_CRLF_PLATFORM && (CVSProviderPlugin.getPlugin().isUsePlatformLineend());
312         
313                 /*** determine the resources to be committed and/or admin'd ***/
314                 for (Iterator it = changeSet.entrySet().iterator(); it.hasNext();) {
315                     Map.Entry entry = (Map.Entry) it.next();
316                     IFile file = (IFile) entry.getKey();
317                     KSubstOption toKSubst = (KSubstOption) entry.getValue();
318
319                     // only set keyword substitution if resource is a managed file
320
checkIsChild(file);
321                     ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor(file);
322                     if (! mFile.isManaged()) continue;
323                     
324                     // only set keyword substitution if new differs from actual
325
byte[] syncBytes = mFile.getSyncBytes();
326                     KSubstOption fromKSubst = ResourceSyncInfo.getKeywordMode(syncBytes);
327                     if (toKSubst.equals(fromKSubst)) continue;
328                     
329                     // change resource sync info immediately for an outgoing addition
330
if (ResourceSyncInfo.isAddition(syncBytes)) {
331                         mFile.setSyncBytes(ResourceSyncInfo.setKeywordMode(syncBytes, toKSubst), ICVSFile.UNKNOWN);
332                         continue;
333                     }
334
335                     // nothing do to for deletions
336
if (ResourceSyncInfo.isDeletion(syncBytes)) continue;
337
338                     // file exists remotely so we'll have to commit it
339
if (fromKSubst.isBinary() && ! toKSubst.isBinary()) {
340                         // converting from binary to text
341
cleanLineDelimiters(file, useCRLF, new NullProgressMonitor()); // XXX need better progress monitoring
342
// remember to commit the cleaned resource as text before admin
343
filesToCommitAsText.add(mFile);
344                     }
345                     // remember to admin the resource
346
List list = (List) filesToAdmin.get(toKSubst);
347                     if (list == null) {
348                         list = new ArrayList();
349                         filesToAdmin.put(toKSubst, list);
350                     }
351                     list.add(mFile);
352                 }
353             
354                 /*** commit then admin the resources ***/
355                 // compute the total work to be performed
356
int totalWork = filesToCommitAsText.size() + 1;
357                 for (Iterator it = filesToAdmin.values().iterator(); it.hasNext();) {
358                     List list = (List) it.next();
359                     totalWork += list.size();
360                     totalWork += 1; // Add 1 for each connection that needs to be made
361
}
362                 if (totalWork != 0) {
363                     monitor.beginTask(CVSMessages.CVSTeamProvider_settingKSubst, totalWork);
364                     try {
365                         // commit files that changed from binary to text
366
// NOTE: The files are committed as text with conversions even if the
367
// resource sync info still says "binary".
368
if (filesToCommitAsText.size() != 0) {
369                             Session session = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot(), true /* output to console */);
370                             session.open(Policy.subMonitorFor(monitor, 1), true /* open for modification */);
371                             try {
372                                 String JavaDoc keywordChangeComment = comment;
373                                 if (keywordChangeComment == null || keywordChangeComment.length() == 0)
374                                     keywordChangeComment = CVSMessages.CVSTeamProvider_changingKeywordComment;
375                                 result[0] = Command.COMMIT.execute(
376                                     session,
377                                     Command.NO_GLOBAL_OPTIONS,
378                                     new LocalOption[] { Command.DO_NOT_RECURSE, Commit.FORCE,
379                                         Command.makeArgumentOption(Command.MESSAGE_OPTION, keywordChangeComment) },
380                                     (ICVSResource[]) filesToCommitAsText.toArray(new ICVSResource[filesToCommitAsText.size()]),
381                                     filesToCommitAsText,
382                                     null,
383                                     Policy.subMonitorFor(monitor, filesToCommitAsText.size()));
384                             } finally {
385                                 session.close();
386                             }
387
388                             // if errors were encountered, abort
389
if (! result[0].isOK()) return;
390                         }
391                         
392                         // admin files that changed keyword substitution mode
393
// NOTE: As confirmation of the completion of a command, the server replies
394
// with the RCS command output if a change took place. Rather than
395
// assume that the command succeeded, we listen for these lines
396
// and update the local ResourceSyncInfo for the particular files that
397
// were actually changed remotely.
398
for (Iterator it = filesToAdmin.entrySet().iterator(); it.hasNext();) {
399                             Map.Entry entry = (Map.Entry) it.next();
400                             final KSubstOption toKSubst = (KSubstOption) entry.getKey();
401                             final List list = (List) entry.getValue();
402                             // do it
403
Session session = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot(), true /* output to console */);
404                             session.open(Policy.subMonitorFor(monitor, 1), true /* open for modification */);
405                             try {
406                                 result[0] = Command.ADMIN.execute(
407                                     session,
408                                     Command.NO_GLOBAL_OPTIONS,
409                                     new LocalOption[] { toKSubst },
410                                     (ICVSResource[]) list.toArray(new ICVSResource[list.size()]),
411                                     new AdminKSubstListener(toKSubst),
412                                     Policy.subMonitorFor(monitor, list.size()));
413                             } finally {
414                                 session.close();
415                             }
416                             // if errors were encountered, abort
417
if (! result[0].isOK()) return;
418                         }
419                     } finally {
420                         monitor.done();
421                     }
422                 }
423             }
424         }, Policy.monitorFor(monitor));
425         return result[0];
426     }
427     
428     /**
429      * This method translates the contents of a file from binary into text (ASCII).
430      * Fixes the line delimiters in the local file to reflect the platform's
431      * native encoding. Performs CR/LF -> LF or LF -> CR/LF conversion
432      * depending on the platform but does not affect delimiters that are
433      * already correctly encoded.
434      */

435     public static void cleanLineDelimiters(IFile file, boolean useCRLF, IProgressMonitor progress)
436         throws CVSException {
437         try {
438             // convert delimiters in memory
439
ByteArrayOutputStream bos = new ByteArrayOutputStream();
440             InputStream is = new BufferedInputStream(file.getContents());
441             try {
442                 // Always convert CR/LF into LFs
443
is = new CRLFtoLFInputStream(is);
444                 if (useCRLF) {
445                     // For CR/LF platforms, translate LFs to CR/LFs
446
is = new LFtoCRLFInputStream(is);
447                 }
448                 for (int b; (b = is.read()) != -1;) bos.write(b);
449                 bos.close();
450             } finally {
451                 is.close();
452             }
453             // write file back to disk with corrected delimiters if changes were made
454
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
455             file.setContents(bis, false /*force*/, false /*keepHistory*/, progress);
456         } catch (CoreException e) {
457             throw CVSException.wrapException(file, CVSMessages.CVSTeamProvider_cleanLineDelimitersException, e);
458         } catch (IOException e) {
459             throw CVSException.wrapException(file, CVSMessages.CVSTeamProvider_cleanLineDelimitersException, e);
460         }
461     }
462     
463     /*
464      * @see RepositoryProvider#getID()
465      */

466     public String JavaDoc getID() {
467         return CVSProviderPlugin.getTypeId();
468     }
469     
470     /*
471      * @see RepositoryProvider#getMoveDeleteHook()
472      */

473     public IMoveDeleteHook getMoveDeleteHook() {
474         return moveDeleteHook;
475     }
476     
477     /* (non-Javadoc)
478      * @see org.eclipse.team.core.RepositoryProvider#getFileModificationValidator()
479      */

480     public IFileModificationValidator getFileModificationValidator() {
481         return getFileModificationValidator2();
482     }
483     
484     /* (non-Javadoc)
485      * @see org.eclipse.team.core.RepositoryProvider#getFileModificationValidator2()
486      */

487     public FileModificationValidator getFileModificationValidator2() {
488         return internalGetFileModificationValidator();
489     }
490     
491     /**
492      * Checkout (cvs edit) the provided resources so they can be modified locally and committed.
493      * This will make any read-only resources in the list writable and will notify the server
494      * that the file is being edited. This notification may be done immediately or at some
495      * later point depending on whether contact with the server is possble at the time of
496      * invocation or the value of the notify server parameter.
497      *
498      * The recurse parameter is equivalent to the cvs local options -l (<code>true</code>) and
499      * -R (<code>false</code>). The notifyServer parameter can be used to defer server contact
500      * until the next command. This may be approrpiate if no shell or progress monitor is available
501      * to the caller. The notification bit field indicates what temporary watches are to be used while
502      * the file is being edited. The possible values that can be ORed together are ICVSFile.EDIT,
503      * ICVSFile.UNEDIT and ICVSFile.COMMIT. There pre-ORed convenience values ICVSFile.NO_NOTIFICATION
504      * and ICVSFile.NOTIFY_ON_ALL are also available.
505      *
506      * @param resources the resources to be edited
507      * @param recurse indicates whether to recurse (-R) or not (-l)
508      * @param notifyServer indicates whether to notify the server now, if possible,
509      * or defer until the next command.
510      * @param notifyForWrittable
511      * @param notification the temporary watches.
512      * @param progress progress monitor to provide progress indication/cancellation or <code>null</code>
513      * @exception CVSException if this method fails.
514      * @since 2.1
515      *
516      * @see CVSTeamProvider#unedit
517      */

518     public void edit(IResource[] resources, boolean recurse, boolean notifyServer, final boolean notifyForWritable, final int notification, IProgressMonitor progress) throws CVSException {
519         final int notify;
520         if (notification == ICVSFile.NO_NOTIFICATION) {
521             if (CVSProviderPlugin.getPlugin().isWatchOnEdit()) {
522                 notify = ICVSFile.NOTIFY_ON_ALL;
523             } else {
524                 notify = ICVSFile.NO_NOTIFICATION;
525             }
526         } else {
527             notify = notification;
528         }
529         notifyEditUnedit(resources, recurse, notifyServer, new ICVSResourceVisitor() {
530             public void visitFile(ICVSFile file) throws CVSException {
531                 if (notifyForWritable || file.isReadOnly())
532                     file.edit(notify, notifyForWritable, Policy.monitorFor(null));
533             }
534             public void visitFolder(ICVSFolder folder) throws CVSException {
535                 // nothing needs to be done here as the recurse will handle the traversal
536
}
537         }, null /* no scheduling rule */, progress);
538     }
539     
540     /**
541      * Unedit the given resources. Any writtable resources will be reverted to their base contents
542      * and made read-only and the server will be notified that the file is no longer being edited.
543      * This notification may be done immediately or at some
544      * later point depending on whether contact with the server is possble at the time of
545      * invocation or the value of the notify server parameter.
546      *
547      * The recurse parameter is equivalent to the cvs local options -l (<code>true</code>) and
548      * -R (<code>false</code>). The notifyServer parameter can be used to defer server contact
549      * until the next command. This may be approrpiate if no shell or progress monitor is available
550      * to the caller.
551      *
552      * @param resources the resources to be unedited
553      * @param recurse indicates whether to recurse (-R) or not (-l)
554      * @param notifyServer indicates whether to notify the server now, if possible,
555      * or defer until the next command.
556      * @param progress progress monitor to provide progress indication/cancellation or <code>null</code>
557      * @exception CVSException if this method fails.
558      * @since 2.1
559      *
560      * @see CVSTeamProvider#edit
561      */

562     public void unedit(IResource[] resources, boolean recurse, boolean notifyServer, IProgressMonitor progress) throws CVSException {
563         notifyEditUnedit(resources, recurse, notifyServer, new ICVSResourceVisitor() {
564             public void visitFile(ICVSFile file) throws CVSException {
565                 if (!file.isReadOnly())
566                     file.unedit(Policy.monitorFor(null));
567             }
568             public void visitFolder(ICVSFolder folder) throws CVSException {
569                 // nothing needs to be done here as the recurse will handle the traversal
570
}
571         }, getProject() /* project scheduling rule */, progress);
572     }
573     
574     /*
575      * This method captures the common behavior between the edit and unedit methods.
576      */

577     private void notifyEditUnedit(final IResource[] resources, final boolean recurse, final boolean notifyServer, final ICVSResourceVisitor editUneditVisitor, ISchedulingRule rule, IProgressMonitor monitor) throws CVSException {
578         final CVSException[] exception = new CVSException[] { null };
579         IWorkspaceRunnable workspaceRunnable = new IWorkspaceRunnable() {
580             public void run(IProgressMonitor monitor) throws CoreException {
581                 final ICVSResource[] cvsResources = getCVSArguments(resources);
582                 
583                 // mark the files locally as being checked out
584
try {
585                     for (int i = 0; i < cvsResources.length; i++) {
586                         cvsResources[i].accept(editUneditVisitor, recurse);
587                     }
588                 } catch (CVSException e) {
589                     exception[0] = e;
590                     return;
591                 }
592                 
593                 // send the noop command to the server in order to deliver the notifications
594
if (notifyServer) {
595                     monitor.beginTask(null, 100);
596                     Session session = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot(), true);
597                     try {
598                         try {
599                             session.open(Policy.subMonitorFor(monitor, 10), true /* open for modification */);
600                         } catch (CVSException e1) {
601                             // If the connection cannot be opened, just exit normally.
602
// The notifications will be sent when a connection can be made
603
return;
604                         }
605                         Command.NOOP.execute(
606                             session,
607                             Command.NO_GLOBAL_OPTIONS,
608                             Command.NO_LOCAL_OPTIONS,
609                             cvsResources,
610                             null,
611                             Policy.subMonitorFor(monitor, 90));
612                     } catch (CVSException e) {
613                         exception[0] = e;
614                     } finally {
615                         session.close();
616                         monitor.done();
617                     }
618                 }
619             }
620         };
621         try {
622             ResourcesPlugin.getWorkspace().run(workspaceRunnable, rule, 0, Policy.monitorFor(monitor));
623         } catch (CoreException e) {
624             if (exception[0] == null) {
625                 throw CVSException.wrapException(e);
626             } else {
627                 CVSProviderPlugin.log(CVSException.wrapException(e));
628             }
629         }
630         if (exception[0] != null) {
631             throw exception[0];
632         }
633     }
634     
635     /**
636      * Gets the etchAbsentDirectories.
637      * @return Returns a boolean
638      */

639     public boolean getFetchAbsentDirectories() throws CVSException {
640         try {
641             String JavaDoc property = getProject().getPersistentProperty(FETCH_ABSENT_DIRECTORIES_PROP_KEY);
642             if (property == null) return CVSProviderPlugin.getPlugin().getFetchAbsentDirectories();
643             return Boolean.valueOf(property).booleanValue();
644         } catch (CoreException e) {
645             throw new CVSException(new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.CVSTeamProvider_errorGettingFetchProperty, new String JavaDoc[] { project.getName() }), e, project));
646         }
647     }
648     
649     /**
650      * Sets the fetchAbsentDirectories.
651      * @param etchAbsentDirectories The etchAbsentDirectories to set
652      */

653     public void setFetchAbsentDirectories(boolean fetchAbsentDirectories) throws CVSException {
654         internalSetFetchAbsentDirectories(fetchAbsentDirectories ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
655     }
656
657     public void internalSetFetchAbsentDirectories(String JavaDoc fetchAbsentDirectories) throws CVSException {
658         try {
659             getProject().setPersistentProperty(FETCH_ABSENT_DIRECTORIES_PROP_KEY, fetchAbsentDirectories);
660         } catch (CoreException e) {
661             IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.CVSTeamProvider_errorSettingFetchProperty, new String JavaDoc[] { project.getName() }), e, project);
662             throw new CVSException(status);
663         }
664     }
665     
666     /**
667      * @see org.eclipse.team.core.RepositoryProvider#canHandleLinkedResources()
668      */

669     public boolean canHandleLinkedResources() {
670         return true;
671     }
672     
673     /* (non-Javadoc)
674      * @see org.eclipse.team.core.RepositoryProvider#canHandleLinkedResourceURI()
675      */

676     public boolean canHandleLinkedResourceURI() {
677         return true;
678     }
679
680     /* (non-Javadoc)
681      * @see org.eclipse.team.core.RepositoryProvider#validateCreateLink(org.eclipse.core.resources.IResource, int, org.eclipse.core.runtime.IPath)
682      */

683     public IStatus validateCreateLink(IResource resource, int updateFlags, IPath location) {
684         return internalValidateCreateLink(resource);
685     }
686
687     private IStatus internalValidateCreateLink(IResource resource) {
688         ICVSFolder cvsFolder = CVSWorkspaceRoot.getCVSFolderFor(resource.getParent().getFolder(new Path(resource.getName())));
689         try {
690             if (cvsFolder.isCVSFolder()) {
691                 // There is a remote folder that overlaps with the link so disallow
692
return new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.CVSTeamProvider_overlappingRemoteFolder, new String JavaDoc[] { resource.getFullPath().toString() }),resource);
693             } else {
694                 ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(resource.getParent().getFile(new Path(resource.getName())));
695                 if (cvsFile.isManaged()) {
696                     // there is an outgoing file deletion that overlaps the link so disallow
697
return new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.CVSTeamProvider_overlappingFileDeletion, new String JavaDoc[] { resource.getFullPath().toString() }),resource);
698                 }
699             }
700         } catch (CVSException e) {
701             CVSProviderPlugin.log(e);
702             return e.getStatus();
703         }
704         return Status.OK_STATUS;
705     }
706     
707     /* (non-Javadoc)
708      * @see org.eclipse.team.core.RepositoryProvider#validateCreateLink(org.eclipse.core.resources.IResource, int, java.net.URI)
709      */

710     public IStatus validateCreateLink(IResource resource, int updateFlags, URI JavaDoc location) {
711         return internalValidateCreateLink(resource);
712     }
713     
714     /**
715      * Get the editors of the resources by calling the <code>cvs editors</code> command.
716      *
717      * @author <a HREF="mailto:gregor.kohlwes@csc.com,kohlwes@gmx.net">Gregor Kohlwes</a>
718      * @param resources
719      * @param progress
720      * @return IEditorsInfo[]
721      * @throws CVSException
722      */

723     public EditorsInfo[] editors(
724         IResource[] resources,
725         IProgressMonitor progress)
726         throws CVSException {
727
728         // Build the local options
729
LocalOption[] commandOptions = new LocalOption[] {
730         };
731         progress.worked(10);
732         // Build the arguments list
733
String JavaDoc[] arguments = getValidArguments(resources, commandOptions);
734
735         // Build the listener for the command
736
EditorsListener listener = new EditorsListener();
737
738         // Check if canceled
739
if (progress.isCanceled()) {
740             return new EditorsInfo[0];
741         }
742         // Build the session
743
Session session =
744             new Session(
745                 workspaceRoot.getRemoteLocation(),
746                 workspaceRoot.getLocalRoot());
747
748         // Check if canceled
749
if (progress.isCanceled()) {
750             return new EditorsInfo[0];
751         }
752         progress.beginTask(null, 100);
753         try {
754             // Opening the session takes 20% of the time
755
session.open(Policy.subMonitorFor(progress, 20), false /* read-only */);
756
757             if (!progress.isCanceled()) {
758                 // Execute the editors command
759
Command.EDITORS.execute(
760                     session,
761                     Command.NO_GLOBAL_OPTIONS,
762                     commandOptions,
763                     arguments,
764                     listener,
765                     Policy.subMonitorFor(progress, 80));
766             }
767         } finally {
768             session.close();
769             progress.done();
770         }
771         // Return the infos about the editors
772
return listener.getEditorsInfos();
773     }
774
775     /**
776      * Return the commit comment template that was provided by the server.
777      *
778      * @return String
779      * @throws CVSException
780      */

781     public String JavaDoc getCommitTemplate() throws CVSException {
782         ICVSFolder localFolder = getCVSWorkspaceRoot().getLocalRoot();
783         ICVSFile templateFile = CVSWorkspaceRoot.getCVSFileFor(
784             SyncFileWriter.getTemplateFile(
785                 (IContainer)localFolder.getIResource()));
786         if (!templateFile.exists()) return null;
787         InputStream in = new BufferedInputStream(templateFile.getContents());
788         try {
789             ByteArrayOutputStream out = new ByteArrayOutputStream();
790             int b;
791             do {
792                 b = in.read();
793                 if (b != -1)
794                     out.write((byte)b);
795             } while (b != -1);
796             out.close();
797             return new String JavaDoc(out.toString());
798         } catch (IOException e) {
799             throw CVSException.wrapException(e);
800         } finally {
801             try {
802                 in.close();
803             } catch (IOException e) {
804                 // Since we already have the contents, just log this exception
805
CVSProviderPlugin.log(CVSException.wrapException(e));
806             }
807         }
808     }
809     
810     /**
811      * Return true if the project is configured to use watch/edit. A project will use
812      * watch/edit if it was checked out when the global preference to use watch/edit is
813      * turned on.
814      * @return boolean
815      */

816     public boolean isWatchEditEnabled() throws CVSException {
817         IProject project = getProject();
818         try {
819             String JavaDoc property = (String JavaDoc)project.getSessionProperty(WATCH_EDIT_PROP_KEY);
820             if (property == null) {
821                 property = project.getPersistentProperty(WATCH_EDIT_PROP_KEY);
822                 if (property == null) {
823                     // The persistant property for the project was never set (i.e. old project)
824
// Use the global preference to determine if the project is using watch/edit
825
return CVSProviderPlugin.getPlugin().isWatchEditEnabled();
826                 } else {
827                     project.setSessionProperty(WATCH_EDIT_PROP_KEY, property);
828                 }
829             }
830             return Boolean.valueOf(property).booleanValue();
831         } catch (CoreException e) {
832             if (project.isAccessible()) {
833                 // We only care if the project still exists
834
IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.CVSTeamProvider_errorGettingWatchEdit, new String JavaDoc[] { project.getName() }), e, project);
835                 throw new CVSException(status);
836             }
837         }
838         return false;
839     }
840     
841     public void setWatchEditEnabled(boolean enabled) throws CVSException {
842         internalSetWatchEditEnabled(enabled ? Boolean.TRUE.toString() : Boolean.FALSE.toString());
843     }
844     
845     private void internalSetWatchEditEnabled(String JavaDoc enabled) throws CVSException {
846         try {
847             IProject project = getProject();
848             project.setPersistentProperty(WATCH_EDIT_PROP_KEY, enabled);
849             project.setSessionProperty(WATCH_EDIT_PROP_KEY, enabled);
850         } catch (CoreException e) {
851             IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.CVSTeamProvider_errorSettingWatchEdit, new String JavaDoc[] { project.getName() }), e, project);
852             throw new CVSException(status);
853         }
854     }
855     
856     /* (non-Javadoc)
857      * @see org.eclipse.team.core.RepositoryProvider#getRuleFactory()
858      */

859     public IResourceRuleFactory getRuleFactory() {
860         return RESOURCE_RULE_FACTORY;
861     }
862
863     /* (non-Javadoc)
864      * @see org.eclipse.team.core.RepositoryProvider#getFileHistoryProvider()
865      */

866     public IFileHistoryProvider getFileHistoryProvider() {
867            if (CVSTeamProvider.fileHistoryProvider == null) {
868                 CVSTeamProvider.fileHistoryProvider = new CVSFileHistoryProvider();
869             }
870             return CVSTeamProvider.fileHistoryProvider;
871     }
872 }
873
Popular Tags