1 11 package org.eclipse.team.internal.ccvs.core; 12 13 import java.io.*; 14 import java.net.URI ; 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 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' }); 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 private final static QualifiedName FETCH_ABSENT_DIRECTORIES_PROP_KEY = 59 new QualifiedName("org.eclipse.team.cvs.core", "fetch_absent_directories"); private final static QualifiedName WATCH_EDIT_PROP_KEY = 62 new QualifiedName("org.eclipse.team.cvs.core", "watch_edit"); 64 68 private static final QualifiedName TEMP_SHARED = new QualifiedName(CVSProviderPlugin.ID, "tempShare"); 70 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 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 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 113 protected static CVSCoreFileModificationValidator internalGetFileModificationValidator() { 114 if (CVSTeamProvider.fileModificationValidator == null) { 115 CVSTeamProvider.fileModificationValidator = new CVSCoreFileModificationValidator(); 116 } 117 return CVSTeamProvider.fileModificationValidator; 118 } 119 120 123 public CVSTeamProvider() { 124 } 125 126 129 public void deconfigure() { 130 } 131 132 135 public void deconfigured() { 136 try { 140 EclipseSynchronizer.getInstance().deconfigure(getProject(), null); 141 internalSetWatchEditEnabled(null); 142 internalSetFetchAbsentDirectories(null); 143 } catch(CVSException e) { 144 CVSProviderPlugin.log(e); 146 } 147 ResourceStateChangeListeners.getListener().projectDeconfigured(getProject()); 148 } 149 152 public IProject getProject() { 153 return project; 154 } 155 156 159 public void setProject(IProject project) { 160 this.project = project; 161 this.workspaceRoot = new CVSWorkspaceRoot(project); 162 } 166 167 170 public ICVSRepositoryLocation getRemoteLocation() throws CVSException { 171 try { 172 return workspaceRoot.getRemoteLocation(); 173 } catch (CVSException e) { 174 try { 176 RepositoryProvider.unmap(project); 177 } catch (TeamException ex) { 178 CVSProviderPlugin.log(ex); 179 } 180 throw e; 182 } 183 } 184 185 public CVSWorkspaceRoot getCVSWorkspaceRoot() { 186 return workspaceRoot; 187 } 188 189 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 [] {resource.getFullPath().toString(), project.getName()})), 196 null)); 197 } 198 199 202 private String [] 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 [])arguments.toArray(new String [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 227 public void setRemoteRoot(ICVSRepositoryLocation location, IProgressMonitor monitor) throws TeamException { 228 229 final String 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 progress.beginTask(null, 100); 240 final IProgressMonitor monitor = Policy.infiniteSubMonitorFor(progress, 100); 241 monitor.beginTask(null, 256); 242 243 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 [] { 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 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 303 public IStatus setKeywordSubstitution(final Map changeSet, 304 final String 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 filesToAdmin = new HashMap(); 310 final Collection filesToCommitAsText = new HashSet(); final boolean useCRLF = IS_CRLF_PLATFORM && (CVSProviderPlugin.getPlugin().isUsePlatformLineend()); 312 313 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 checkIsChild(file); 321 ICVSFile mFile = CVSWorkspaceRoot.getCVSFileFor(file); 322 if (! mFile.isManaged()) continue; 323 324 byte[] syncBytes = mFile.getSyncBytes(); 326 KSubstOption fromKSubst = ResourceSyncInfo.getKeywordMode(syncBytes); 327 if (toKSubst.equals(fromKSubst)) continue; 328 329 if (ResourceSyncInfo.isAddition(syncBytes)) { 331 mFile.setSyncBytes(ResourceSyncInfo.setKeywordMode(syncBytes, toKSubst), ICVSFile.UNKNOWN); 332 continue; 333 } 334 335 if (ResourceSyncInfo.isDeletion(syncBytes)) continue; 337 338 if (fromKSubst.isBinary() && ! toKSubst.isBinary()) { 340 cleanLineDelimiters(file, useCRLF, new NullProgressMonitor()); filesToCommitAsText.add(mFile); 344 } 345 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 355 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; } 362 if (totalWork != 0) { 363 monitor.beginTask(CVSMessages.CVSTeamProvider_settingKSubst, totalWork); 364 try { 365 if (filesToCommitAsText.size() != 0) { 369 Session session = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot(), true ); 370 session.open(Policy.subMonitorFor(monitor, 1), true ); 371 try { 372 String 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 (! result[0].isOK()) return; 390 } 391 392 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 Session session = new Session(workspaceRoot.getRemoteLocation(), workspaceRoot.getLocalRoot(), true ); 404 session.open(Policy.subMonitorFor(monitor, 1), true ); 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 (! result[0].isOK()) return; 418 } 419 } finally { 420 monitor.done(); 421 } 422 } 423 } 424 }, Policy.monitorFor(monitor)); 425 return result[0]; 426 } 427 428 435 public static void cleanLineDelimiters(IFile file, boolean useCRLF, IProgressMonitor progress) 436 throws CVSException { 437 try { 438 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 440 InputStream is = new BufferedInputStream(file.getContents()); 441 try { 442 is = new CRLFtoLFInputStream(is); 444 if (useCRLF) { 445 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 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 455 file.setContents(bis, false , false , 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 466 public String getID() { 467 return CVSProviderPlugin.getTypeId(); 468 } 469 470 473 public IMoveDeleteHook getMoveDeleteHook() { 474 return moveDeleteHook; 475 } 476 477 480 public IFileModificationValidator getFileModificationValidator() { 481 return getFileModificationValidator2(); 482 } 483 484 487 public FileModificationValidator getFileModificationValidator2() { 488 return internalGetFileModificationValidator(); 489 } 490 491 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 } 537 }, null , progress); 538 } 539 540 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 } 571 }, getProject() , progress); 572 } 573 574 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 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 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 ); 600 } catch (CVSException e1) { 601 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 639 public boolean getFetchAbsentDirectories() throws CVSException { 640 try { 641 String 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 [] { project.getName() }), e, project)); 646 } 647 } 648 649 653 public void setFetchAbsentDirectories(boolean fetchAbsentDirectories) throws CVSException { 654 internalSetFetchAbsentDirectories(fetchAbsentDirectories ? Boolean.TRUE.toString() : Boolean.FALSE.toString()); 655 } 656 657 public void internalSetFetchAbsentDirectories(String 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 [] { project.getName() }), e, project); 662 throw new CVSException(status); 663 } 664 } 665 666 669 public boolean canHandleLinkedResources() { 670 return true; 671 } 672 673 676 public boolean canHandleLinkedResourceURI() { 677 return true; 678 } 679 680 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 return new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.CVSTeamProvider_overlappingRemoteFolder, new String [] { resource.getFullPath().toString() }),resource); 693 } else { 694 ICVSFile cvsFile = CVSWorkspaceRoot.getCVSFileFor(resource.getParent().getFile(new Path(resource.getName()))); 695 if (cvsFile.isManaged()) { 696 return new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.CVSTeamProvider_overlappingFileDeletion, new String [] { 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 710 public IStatus validateCreateLink(IResource resource, int updateFlags, URI location) { 711 return internalValidateCreateLink(resource); 712 } 713 714 723 public EditorsInfo[] editors( 724 IResource[] resources, 725 IProgressMonitor progress) 726 throws CVSException { 727 728 LocalOption[] commandOptions = new LocalOption[] { 730 }; 731 progress.worked(10); 732 String [] arguments = getValidArguments(resources, commandOptions); 734 735 EditorsListener listener = new EditorsListener(); 737 738 if (progress.isCanceled()) { 740 return new EditorsInfo[0]; 741 } 742 Session session = 744 new Session( 745 workspaceRoot.getRemoteLocation(), 746 workspaceRoot.getLocalRoot()); 747 748 if (progress.isCanceled()) { 750 return new EditorsInfo[0]; 751 } 752 progress.beginTask(null, 100); 753 try { 754 session.open(Policy.subMonitorFor(progress, 20), false ); 756 757 if (!progress.isCanceled()) { 758 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 listener.getEditorsInfos(); 773 } 774 775 781 public String 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 (out.toString()); 798 } catch (IOException e) { 799 throw CVSException.wrapException(e); 800 } finally { 801 try { 802 in.close(); 803 } catch (IOException e) { 804 CVSProviderPlugin.log(CVSException.wrapException(e)); 806 } 807 } 808 } 809 810 816 public boolean isWatchEditEnabled() throws CVSException { 817 IProject project = getProject(); 818 try { 819 String property = (String )project.getSessionProperty(WATCH_EDIT_PROP_KEY); 820 if (property == null) { 821 property = project.getPersistentProperty(WATCH_EDIT_PROP_KEY); 822 if (property == null) { 823 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 IStatus status = new CVSStatus(IStatus.ERROR, CVSStatus.ERROR, NLS.bind(CVSMessages.CVSTeamProvider_errorGettingWatchEdit, new String [] { 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 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 [] { project.getName() }), e, project); 852 throw new CVSException(status); 853 } 854 } 855 856 859 public IResourceRuleFactory getRuleFactory() { 860 return RESOURCE_RULE_FACTORY; 861 } 862 863 866 public IFileHistoryProvider getFileHistoryProvider() { 867 if (CVSTeamProvider.fileHistoryProvider == null) { 868 CVSTeamProvider.fileHistoryProvider = new CVSFileHistoryProvider(); 869 } 870 return CVSTeamProvider.fileHistoryProvider; 871 } 872 } 873
| Popular Tags
|