1 11 package org.eclipse.team.internal.ccvs.ui.subscriber; 12 13 import java.util.*; 14 import org.eclipse.compare.structuremergeviewer.IDiffContainer; 15 import org.eclipse.compare.structuremergeviewer.IDiffElement; 16 import org.eclipse.core.resources.IResource; 17 import org.eclipse.core.runtime.*; 18 import org.eclipse.core.runtime.jobs.Job; 19 import org.eclipse.jface.action.Action; 20 import org.eclipse.jface.action.MenuManager; 21 import org.eclipse.jface.dialogs.IDialogSettings; 22 import org.eclipse.jface.resource.ImageDescriptor; 23 import org.eclipse.jface.viewers.*; 24 import org.eclipse.swt.custom.BusyIndicator; 25 import org.eclipse.team.core.TeamException; 26 import org.eclipse.team.core.subscribers.Subscriber; 27 import org.eclipse.team.core.synchronize.*; 28 import org.eclipse.team.core.variants.IResourceVariant; 29 import org.eclipse.team.internal.ccvs.core.*; 30 import org.eclipse.team.internal.ccvs.core.resources.*; 31 import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo; 32 import org.eclipse.team.internal.ccvs.ui.*; 33 import org.eclipse.team.internal.ccvs.ui.Policy; 34 import org.eclipse.team.internal.ccvs.ui.operations.RemoteLogOperation; 35 import org.eclipse.team.internal.ui.TeamUIPlugin; 36 import org.eclipse.team.internal.ui.Utils; 37 import org.eclipse.team.internal.ui.synchronize.*; 38 import org.eclipse.team.ui.synchronize.*; 39 import org.eclipse.ui.progress.UIJob; 40 41 53 public class ChangeLogModelProvider extends SynchronizeModelProvider { 54 private RemoteLogOperation logOperation; 57 58 private boolean shutdown = false; 60 private FetchLogEntriesJob fetchLogEntriesJob; 61 62 private ChangeLogActionGroup sortGroup; 64 65 private CVSTag tag1; 68 private CVSTag tag2; 69 private Map multipleResourceMap; 70 71 private final static String SORT_ORDER_GROUP = "changelog_sort"; private static final String P_LAST_COMMENTSORT = TeamUIPlugin.ID + ".P_LAST_COMMENT_SORT"; private static final String P_LAST_RESOURCESORT = TeamUIPlugin.ID + ".P_LAST_RESOURCE_SORT"; 76 79 private class ToggleSortOrderAction extends Action { 80 private int criteria; 81 private int sortType; 82 public final static int RESOURCE_NAME = 1; 83 public final static int COMMENT = 2; 84 protected ToggleSortOrderAction(String name, int criteria, int sortType, int defaultCriteria) { 85 super(name, Action.AS_RADIO_BUTTON); 86 this.criteria = criteria; 87 this.sortType = sortType; 88 setChecked(criteria == defaultCriteria); 89 } 90 91 public void run() { 92 StructuredViewer viewer = getViewer(); 93 if (viewer != null && !viewer.getControl().isDisposed()) { 94 ChangeLogModelSorter sorter = (ChangeLogModelSorter) viewer.getSorter(); 95 if (isChecked() && sorter != null && getCriteria(sorter) != criteria) { 96 viewer.setSorter(createSorter(sorter)); 97 String key = sortType == RESOURCE_NAME ? P_LAST_RESOURCESORT : P_LAST_COMMENTSORT; 98 IDialogSettings pageSettings = getConfiguration().getSite().getPageSettings(); 99 if(pageSettings != null) { 100 pageSettings.put(key, criteria); 101 } 102 update(); 103 } 104 } 105 } 106 107 public void update() { 108 StructuredViewer viewer = getViewer(); 109 if (viewer != null && !viewer.getControl().isDisposed()) { 110 ChangeLogModelSorter sorter = (ChangeLogModelSorter) viewer.getSorter(); 111 if (sorter != null) { 112 setChecked(getCriteria(sorter) == criteria); 113 } 114 } 115 } 116 117 protected ChangeLogModelSorter createSorter(ChangeLogModelSorter sorter) { 118 if(sortType == COMMENT) { 119 return new ChangeLogModelSorter(criteria, sorter.getResourceCriteria()); 120 } else { 121 return new ChangeLogModelSorter(sorter.getCommentCriteria(), criteria); 122 } 123 } 124 125 protected int getCriteria(ChangeLogModelSorter sorter) { 126 if(sortType == COMMENT) 127 return sorter.getCommentCriteria(); 128 else 129 return sorter.getResourceCriteria(); 130 } 131 } 132 133 136 public class ChangeLogActionGroup extends SynchronizePageActionGroup { 137 private MenuManager sortByComment; 138 private MenuManager sortByResource; 139 public void initialize(ISynchronizePageConfiguration configuration) { 140 super.initialize(configuration); 141 sortByComment = new MenuManager(Policy.bind("ChangeLogModelProvider.0")); sortByResource = new MenuManager(Policy.bind("ChangeLogModelProvider.6")); 144 appendToGroup( 145 ISynchronizePageConfiguration.P_CONTEXT_MENU, 146 SORT_ORDER_GROUP, 147 sortByComment); 148 appendToGroup( 149 ISynchronizePageConfiguration.P_CONTEXT_MENU, 150 SORT_ORDER_GROUP, 151 sortByResource); 152 153 ChangeLogModelSorter sorter = (ChangeLogModelSorter)getViewerSorter(); 154 155 sortByComment.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.1"), ChangeLogModelSorter.COMMENT, ToggleSortOrderAction.COMMENT, sorter.getCommentCriteria())); sortByComment.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.2"), ChangeLogModelSorter.DATE, ToggleSortOrderAction.COMMENT, sorter.getCommentCriteria())); sortByComment.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.3"), ChangeLogModelSorter.USER, ToggleSortOrderAction.COMMENT, sorter.getCommentCriteria())); 159 sortByResource.add( new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.8"), ChangeLogModelSorter.PATH, ToggleSortOrderAction.RESOURCE_NAME, sorter.getResourceCriteria())); sortByResource.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.7"), ChangeLogModelSorter.NAME, ToggleSortOrderAction.RESOURCE_NAME, sorter.getResourceCriteria())); sortByResource.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.9"), ChangeLogModelSorter.PARENT_NAME, ToggleSortOrderAction.RESOURCE_NAME, sorter.getResourceCriteria())); } 163 164 167 public void dispose() { 168 sortByComment.dispose(); 169 sortByResource.dispose(); 170 sortByComment.removeAll(); 171 sortByResource.removeAll(); 172 super.dispose(); 173 } 174 } 175 176 180 public static class FullPathSyncInfoElement extends SyncInfoModelElement { 181 public FullPathSyncInfoElement(IDiffContainer parent, SyncInfo info) { 182 super(parent, info); 183 } 184 public String getName() { 185 IResource resource = getResource(); 186 return resource.getName() + " - " + resource.getFullPath().toString(); } 188 } 189 190 193 public class CVSUpdatableSyncInfo extends CVSSyncInfo { 194 public int kind; 195 public CVSUpdatableSyncInfo(int kind, IResource local, IResourceVariant base, IResourceVariant remote, Subscriber s) { 196 super(local, base, remote, s); 197 this.kind = kind; 198 } 199 200 protected int calculateKind() throws TeamException { 201 return kind; 202 } 203 } 204 205 208 209 private class FetchLogEntriesJob extends Job { 210 private Set syncSets = new HashSet(); 211 public FetchLogEntriesJob() { 212 super(Policy.bind("ChangeLogModelProvider.4")); setUser(false); 214 } 215 public boolean belongsTo(Object family) { 216 return family == ISynchronizeManager.FAMILY_SYNCHRONIZE_OPERATION; 217 } 218 public IStatus run(IProgressMonitor monitor) { 219 220 if (syncSets != null && !shutdown) { 221 SyncInfoSet[] updates; 223 synchronized (syncSets) { 224 updates = (SyncInfoSet[]) syncSets.toArray(new SyncInfoSet[syncSets.size()]); 225 syncSets.clear(); 226 } 227 for (int i = 0; i < updates.length; i++) { 228 calculateRoots(updates[i], monitor); 229 } 230 refreshViewer(); 231 } 232 return Status.OK_STATUS; 233 234 } 235 public void add(SyncInfoSet set) { 236 synchronized(syncSets) { 237 syncSets.add(set); 238 } 239 schedule(); 240 } 241 public boolean shouldRun() { 242 return !syncSets.isEmpty(); 243 } 244 }; 245 246 249 public static class ChangeLogModelProviderDescriptor implements ISynchronizeModelProviderDescriptor { 250 public static final String ID = TeamUIPlugin.ID + ".modelprovider_cvs_changelog"; public String getId() { 252 return ID; 253 } 254 public String getName() { 255 return Policy.bind("ChangeLogModelProvider.5"); } 257 public ImageDescriptor getImageDescriptor() { 258 return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_CHANGELOG); 259 } 260 }; 261 private static final ChangeLogModelProviderDescriptor descriptor = new ChangeLogModelProviderDescriptor(); 262 263 public ChangeLogModelProvider(ISynchronizePageConfiguration configuration, SyncInfoSet set, CVSTag tag1, CVSTag tag2) { 264 super(configuration, set); 265 this.tag1 = tag1; 266 this.tag2 = tag2; 267 configuration.addMenuGroup(ISynchronizePageConfiguration.P_CONTEXT_MENU, SORT_ORDER_GROUP); 268 this.sortGroup = new ChangeLogActionGroup(); 269 configuration.addActionContribution(sortGroup); 270 } 271 272 275 public ISynchronizeModelProviderDescriptor getDescriptor() { 276 return descriptor; 277 } 278 279 283 protected IDiffElement[] buildModelObjects(ISynchronizeModelElement node) { 284 if (node == getModelRoot()) { 285 try { 287 if (fetchLogEntriesJob != null && fetchLogEntriesJob.getState() != Job.NONE) { 288 fetchLogEntriesJob.cancel(); 289 fetchLogEntriesJob.join(); 290 } 291 } catch (InterruptedException e) { 292 } 293 294 startUpdateJob(getSyncInfoSet()); 296 } 297 return new IDiffElement[0]; 298 } 299 300 private void startUpdateJob(SyncInfoSet set) { 301 if(fetchLogEntriesJob == null) { 302 fetchLogEntriesJob = new FetchLogEntriesJob(); 303 } 304 fetchLogEntriesJob.add(set); 305 } 306 307 private void refreshViewer() { 308 UIJob updateUI = new UIJob("") { public IStatus runInUIThread(IProgressMonitor monitor) { 310 BusyIndicator.showWhile(getDisplay(), new Runnable () { 311 public void run() { 312 StructuredViewer tree = getViewer(); 313 tree.refresh(); 314 ISynchronizeModelElement root = getModelRoot(); 315 if(root instanceof SynchronizeModelElement) 316 ((SynchronizeModelElement)root).fireChanges(); 317 } 318 }); 319 320 return Status.OK_STATUS; 321 } 322 }; 323 updateUI.setSystem(true); 324 updateUI.schedule(); 325 } 326 327 private void calculateRoots(SyncInfoSet set, IProgressMonitor monitor) { 328 try { 329 monitor.beginTask(null, 100); 330 SyncInfo[] infos = set.getSyncInfos(); 332 ArrayList commentNodes = new ArrayList(); 333 ArrayList resourceNodes = new ArrayList(); 334 for (int i = 0; i < infos.length; i++) { 335 SyncInfo info = infos[i]; 336 if(isInterestingChange(info)) { 337 commentNodes.add(info); 338 } else { 339 resourceNodes.add(info); 340 } 341 } 342 for (Iterator it = resourceNodes.iterator(); it.hasNext();) { 344 SyncInfo info = (SyncInfo) it.next(); 345 addNewElementFor(info, null, null); 346 } 347 if(! resourceNodes.isEmpty()) 348 refreshViewer(); 349 350 SyncInfo[] commentInfos = (SyncInfo[]) commentNodes.toArray(new SyncInfo[commentNodes.size()]); 352 RemoteLogOperation logs = getSyncInfoComment(commentInfos, Policy.subMonitorFor(monitor, 80)); 353 addLogEntries(commentInfos, logs, Policy.subMonitorFor(monitor, 20)); 354 } catch (CVSException e) { 355 Utils.handle(e); 356 } catch (InterruptedException e) { 357 } finally { 358 monitor.done(); 359 } 360 } 361 362 366 private void addLogEntries(SyncInfo[] commentInfos, RemoteLogOperation logs, IProgressMonitor monitor) { 367 try { 368 monitor.beginTask(null, commentInfos.length * 10); 369 if (logs != null) { 370 for (int i = 0; i < commentInfos.length; i++) { 371 addSyncInfoToCommentNode(commentInfos[i], logs); 372 monitor.worked(10); 373 } 374 if (getConfiguration().getComparisonType().equals(ISynchronizePageConfiguration.TWO_WAY)) { 376 logs.clearEntries(); 377 } 378 } 379 } finally { 380 monitor.done(); 381 } 382 } 383 384 390 private void addSyncInfoToCommentNode(SyncInfo info, RemoteLogOperation logs) { 391 ICVSRemoteResource remoteResource = getRemoteResource((CVSSyncInfo)info); 392 if(tag1 != null && tag2 != null) { 393 addMultipleRevisions(info, logs, remoteResource); 394 } else { 395 addSingleRevision(info, logs, remoteResource); 396 } 397 } 398 399 406 private void addMultipleRevisions(SyncInfo info, RemoteLogOperation logs, ICVSRemoteResource remoteResource) { 407 ILogEntry[] logEntries = logs.getLogEntries(remoteResource); 408 if(logEntries == null || logEntries.length == 0) { 409 addNewElementFor(info, null, null); 412 } else { 413 for (int i = 0; i < logEntries.length; i++) { 414 ILogEntry entry = logEntries[i]; 415 addNewElementFor(info, remoteResource, entry); 416 } 417 } 418 } 419 420 427 private void addSingleRevision(SyncInfo info, RemoteLogOperation logs, ICVSRemoteResource remoteResource) { 428 ILogEntry logEntry = logs.getLogEntry(remoteResource); 429 try { 432 String remoteRevision = ((ICVSRemoteFile) remoteResource).getRevision(); 433 if (isDeletedRemotely(info)) { 434 ILogEntry[] logEntries = logs.getLogEntries(remoteResource); 435 for (int i = 0; i < logEntries.length; i++) { 436 ILogEntry entry = logEntries[i]; 437 String revision = entry.getRevision(); 438 if (entry.isDeletion() && ResourceSyncInfo.isLaterRevision(revision, remoteRevision)) { 439 logEntry = entry; 440 } 441 } 442 } 443 } catch (TeamException e) { 444 } 446 addNewElementFor(info, remoteResource, logEntry); 447 } 448 449 private boolean isDeletedRemotely(SyncInfo info) { 450 int kind = info.getKind(); 451 if(kind == (SyncInfo.INCOMING | SyncInfo.DELETION)) return true; 452 if(SyncInfo.getDirection(kind) == SyncInfo.CONFLICTING && info.getRemote() == null) return true; 453 return false; 454 } 455 456 private void addNewElementFor(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) { 457 ISynchronizeModelElement element; 458 if(remoteResource != null && logEntry != null && isInterestingChange(info)) { 460 ChangeLogDiffNode changeRoot = getChangeLogDiffNodeFor(logEntry); 461 if (changeRoot == null) { 462 changeRoot = new ChangeLogDiffNode(getModelRoot(), logEntry); 463 addToViewer(changeRoot); 464 } 465 if(requiresCustomSyncInfo(info, remoteResource, logEntry)) { 466 info = new CVSUpdatableSyncInfo(info.getKind(), info.getLocal(), info.getBase(), (RemoteResource)logEntry.getRemoteFile(), ((CVSSyncInfo)info).getSubscriber()); 467 try { 468 info.init(); 469 } catch (TeamException e) { 470 } 472 } 473 element = new FullPathSyncInfoElement(changeRoot, info); 474 } else { 475 element = new FullPathSyncInfoElement(getModelRoot(), info); 478 } 479 addToViewer(element); 480 } 481 482 private boolean requiresCustomSyncInfo(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) { 483 if (logEntry.isDeletion() || !(info instanceof CVSSyncInfo)) return false; 485 IResourceVariant remote = info.getRemote(); 488 if (remote == null) return true; 489 return !remote.equals(remoteResource); 490 } 491 492 496 private ChangeLogDiffNode getChangeLogDiffNodeFor(ILogEntry entry) { 497 IDiffElement[] elements = getModelRoot().getChildren(); 498 for (int i = 0; i < elements.length; i++) { 499 IDiffElement element = elements[i]; 500 if(element instanceof ChangeLogDiffNode) { 501 ChangeLogDiffNode other = (ChangeLogDiffNode)element; 502 ILogEntry thisLog = other.getComment(); 503 if(thisLog.getComment().equals(entry.getComment()) && thisLog.getAuthor().equals(entry.getAuthor())) { 504 return other; 505 } 506 } 507 } 508 return null; 509 } 510 511 514 private boolean isInterestingChange(SyncInfo info) { 515 int kind = info.getKind(); 516 if(info.getLocal().getType() != IResource.FILE) return false; 517 if(info.getComparator().isThreeWay()) { 518 return (kind & SyncInfo.DIRECTION_MASK) != SyncInfo.OUTGOING; 519 } 520 return true; 521 } 522 523 527 private RemoteLogOperation getSyncInfoComment(SyncInfo[] infos, IProgressMonitor monitor) throws CVSException, InterruptedException { 528 List remotes = new ArrayList(); 529 for (int i = 0; i < infos.length; i++) { 530 CVSSyncInfo info = (CVSSyncInfo)infos[i]; 531 if (info.getLocal().getType() != IResource.FILE) { 532 continue; 533 } 534 ICVSRemoteResource remote = getRemoteResource(info); 535 if(remote != null) { 536 remotes.add(remote); 537 } 538 } 539 ICVSRemoteResource[] remoteResources = (ICVSRemoteResource[]) remotes.toArray(new ICVSRemoteResource[remotes.size()]); 540 if(logOperation == null) { 541 logOperation = new RemoteLogOperation(null, remoteResources, tag1, tag2); 542 } 543 if(! remotes.isEmpty()) { 544 logOperation.setRemoteResources(remoteResources); 545 logOperation.execute(monitor); 546 } 547 return logOperation; 548 } 549 550 private ICVSRemoteResource getRemoteResource(CVSSyncInfo info) { 551 try { 552 ICVSRemoteResource remote = (ICVSRemoteResource) info.getRemote(); 553 ICVSRemoteResource local = (ICVSRemoteFile) CVSWorkspaceRoot.getRemoteResourceFor(info.getLocal()); 554 if(local == null) { 555 local = (ICVSRemoteResource)info.getBase(); 556 } 557 558 String remoteRevision = getRevisionString(remote); 559 String localRevision = getRevisionString(local); 560 561 boolean useRemote = true; 562 if (local != null && remote != null) { 563 useRemote = ResourceSyncInfo.isLaterRevision(remoteRevision, localRevision); 564 } else if (remote == null) { 565 useRemote = false; 566 } 567 if (useRemote) { 568 return remote; 569 } else if (local != null) { 570 return local; 571 } 572 return null; 573 } catch (CVSException e) { 574 CVSUIPlugin.log(e); 575 return null; 576 } 577 } 578 579 private String getRevisionString(ICVSRemoteResource remoteFile) { 580 if(remoteFile instanceof RemoteFile) { 581 return ((RemoteFile)remoteFile).getRevision(); 582 } 583 return null; 584 } 585 586 589 public void dispose() { 590 shutdown = true; 591 if(fetchLogEntriesJob != null && fetchLogEntriesJob.getState() != Job.NONE) { 592 fetchLogEntriesJob.cancel(); 593 } 594 sortGroup.dispose(); 595 super.dispose(); 596 } 597 598 601 public ViewerSorter getViewerSorter() { 602 int commentSort = ChangeLogModelSorter.DATE; 603 int resourceSort = ChangeLogModelSorter.PATH; 604 try { 605 IDialogSettings pageSettings = getConfiguration().getSite().getPageSettings(); 606 if(pageSettings != null) { 607 commentSort = pageSettings.getInt(P_LAST_COMMENTSORT); 608 resourceSort = pageSettings.getInt(P_LAST_RESOURCESORT); 609 } 610 } catch(NumberFormatException e) { 611 } 613 return new ChangeLogModelSorter(commentSort, resourceSort); 614 } 615 616 619 protected void doAdd(ISynchronizeModelElement parent, ISynchronizeModelElement element) { 620 AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer(); 621 viewer.add(parent, element); 622 } 623 624 627 protected void doRemove(ISynchronizeModelElement element) { 628 AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer(); 629 viewer.remove(element); 630 } 631 632 635 protected void handleResourceAdditions(ISyncInfoTreeChangeEvent event) { 636 startUpdateJob(new SyncInfoSet(event.getAddedResources())); 637 } 638 639 642 protected void handleResourceChanges(ISyncInfoTreeChangeEvent event) { 643 SyncInfo[] infos = event.getChangedResources(); 645 for (int i = 0; i < infos.length; i++) { 646 SyncInfo info = infos[i]; 647 IResource local = info.getLocal(); 648 removeFromViewer(local); 649 } 650 startUpdateJob(new SyncInfoSet(event.getChangedResources())); 651 } 652 653 656 protected void handleResourceRemovals(ISyncInfoTreeChangeEvent event) { 657 IResource[] removedRoots = event.getRemovedSubtreeRoots(); 658 for (int i = 0; i < removedRoots.length; i++) { 659 removeFromViewer(removedRoots[i]); 660 } 661 IResource[] removedResources = event.getRemovedResources(); 664 for (int i = 0; i < removedResources.length; i++) { 665 removeFromViewer(removedResources[i]); 666 } 667 } 668 669 672 protected void removeFromViewer(IResource resource) { 673 if (logOperation != null) { 675 ISynchronizeModelElement element = getModelObject(resource); 676 if (element instanceof FullPathSyncInfoElement) { 677 CVSSyncInfo info = (CVSSyncInfo) ((FullPathSyncInfoElement) element).getSyncInfo(); 678 if (info != null) { 679 ICVSRemoteResource remote = getRemoteResource(info); 680 logOperation.clearEntriesFor(remote); 681 } 682 } 683 } 684 if(multipleResourceMap != null) { 686 List elements = (List)multipleResourceMap.get(resource); 687 if(elements != null) { 688 for (Iterator it = elements.iterator(); it.hasNext();) { 689 ISynchronizeModelElement element = (ISynchronizeModelElement) it.next(); 690 super.removeFromViewer(element); 691 } 692 multipleResourceMap.remove(resource); 693 } 694 } 695 super.removeFromViewer(resource); 697 } 698 699 702 protected void addToViewer(ISynchronizeModelElement node) { 703 IResource r = node.getResource(); 706 if(r != null) { 707 if(multipleResourceMap == null) { 708 multipleResourceMap = new HashMap(5); 709 } 710 List elements = (List)multipleResourceMap.get(r); 711 if(elements == null) { 712 elements = new ArrayList(2); 713 multipleResourceMap.put(r, elements); 714 } 715 elements.add(node); 716 } 717 super.addToViewer(node); 719 } 720 } 721 | Popular Tags |