blob: cc2cf451a535119f55beefdee0809d89be47089a [file] [log] [blame]
/*******************************************************************************
* 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.subscriber;
import java.util.*;
import org.eclipse.compare.structuremergeviewer.IDiffContainer;
import org.eclipse.compare.structuremergeviewer.IDiffElement;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.core.subscribers.Subscriber;
import org.eclipse.team.core.synchronize.*;
import org.eclipse.team.core.variants.IResourceVariant;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.resources.*;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;
import org.eclipse.team.internal.ccvs.ui.*;
import org.eclipse.team.internal.ccvs.ui.Policy;
import org.eclipse.team.internal.ccvs.ui.operations.RemoteLogOperation;
import org.eclipse.team.internal.ui.TeamUIPlugin;
import org.eclipse.team.internal.ui.Utils;
import org.eclipse.team.internal.ui.synchronize.*;
import org.eclipse.team.ui.synchronize.*;
import org.eclipse.ui.progress.UIJob;
/**
* Disclamer:
* This is a prototype layout using *internal* team classes. It is not meant
* to be an example or sanctioned use of team. These classes and the classes
* references here may change or be deleted in the future.
*
* This provider groups changes into commit sets and fetches the log history for
* files in the background. Changes that can't be grouped into commit sets (e.g. outgoing
* changes) are shown in a flat list.
*
* @since 3.0
*/
public class ChangeLogModelProvider extends SynchronizeModelProvider {
// Log operation that is used to fetch revision histories from the server. It also
// provides caching so we keep it around.
private RemoteLogOperation logOperation;
// Job that builds the layout in the background.
private boolean shutdown = false;
private FetchLogEntriesJob fetchLogEntriesJob;
// Sorters for the commit sets and resources
private ChangeLogActionGroup sortGroup;
// Tag ranges for fetching revision histories. If no tags are specified then
// the history for the remote revision in the sync info is used.
private CVSTag tag1;
private CVSTag tag2;
private Map multipleResourceMap;
// Constants for persisting sorting options
private final static String SORT_ORDER_GROUP = "changelog_sort"; //$NON-NLS-1$
private static final String P_LAST_COMMENTSORT = TeamUIPlugin.ID + ".P_LAST_COMMENT_SORT"; //$NON-NLS-1$
private static final String P_LAST_RESOURCESORT = TeamUIPlugin.ID + ".P_LAST_RESOURCE_SORT"; //$NON-NLS-1$
/* *****************************************************************************
* Action that allows changing the model providers sort order.
*/
private class ToggleSortOrderAction extends Action {
private int criteria;
private int sortType;
public final static int RESOURCE_NAME = 1;
public final static int COMMENT = 2;
protected ToggleSortOrderAction(String name, int criteria, int sortType, int defaultCriteria) {
super(name, Action.AS_RADIO_BUTTON);
this.criteria = criteria;
this.sortType = sortType;
setChecked(criteria == defaultCriteria);
}
public void run() {
StructuredViewer viewer = getViewer();
if (viewer != null && !viewer.getControl().isDisposed()) {
ChangeLogModelSorter sorter = (ChangeLogModelSorter) viewer.getSorter();
if (isChecked() && sorter != null && getCriteria(sorter) != criteria) {
viewer.setSorter(createSorter(sorter));
String key = sortType == RESOURCE_NAME ? P_LAST_RESOURCESORT : P_LAST_COMMENTSORT;
IDialogSettings pageSettings = getConfiguration().getSite().getPageSettings();
if(pageSettings != null) {
pageSettings.put(key, criteria);
}
update();
}
}
}
public void update() {
StructuredViewer viewer = getViewer();
if (viewer != null && !viewer.getControl().isDisposed()) {
ChangeLogModelSorter sorter = (ChangeLogModelSorter) viewer.getSorter();
if (sorter != null) {
setChecked(getCriteria(sorter) == criteria);
}
}
}
protected ChangeLogModelSorter createSorter(ChangeLogModelSorter sorter) {
if(sortType == COMMENT) {
return new ChangeLogModelSorter(criteria, sorter.getResourceCriteria());
} else {
return new ChangeLogModelSorter(sorter.getCommentCriteria(), criteria);
}
}
protected int getCriteria(ChangeLogModelSorter sorter) {
if(sortType == COMMENT)
return sorter.getCommentCriteria();
else
return sorter.getResourceCriteria();
}
}
/* *****************************************************************************
* Action group for this layout. It is added and removed for this layout only.
*/
public class ChangeLogActionGroup extends SynchronizePageActionGroup {
private MenuManager sortByComment;
private MenuManager sortByResource;
public void initialize(ISynchronizePageConfiguration configuration) {
super.initialize(configuration);
sortByComment = new MenuManager(Policy.bind("ChangeLogModelProvider.0")); //$NON-NLS-1$
sortByResource = new MenuManager(Policy.bind("ChangeLogModelProvider.6")); //$NON-NLS-1$
appendToGroup(
ISynchronizePageConfiguration.P_CONTEXT_MENU,
SORT_ORDER_GROUP,
sortByComment);
appendToGroup(
ISynchronizePageConfiguration.P_CONTEXT_MENU,
SORT_ORDER_GROUP,
sortByResource);
ChangeLogModelSorter sorter = (ChangeLogModelSorter)getViewerSorter();
sortByComment.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.1"), ChangeLogModelSorter.COMMENT, ToggleSortOrderAction.COMMENT, sorter.getCommentCriteria())); //$NON-NLS-1$
sortByComment.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.2"), ChangeLogModelSorter.DATE, ToggleSortOrderAction.COMMENT, sorter.getCommentCriteria())); //$NON-NLS-1$
sortByComment.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.3"), ChangeLogModelSorter.USER, ToggleSortOrderAction.COMMENT, sorter.getCommentCriteria())); //$NON-NLS-1$
sortByResource.add( new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.8"), ChangeLogModelSorter.PATH, ToggleSortOrderAction.RESOURCE_NAME, sorter.getResourceCriteria())); //$NON-NLS-1$
sortByResource.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.7"), ChangeLogModelSorter.NAME, ToggleSortOrderAction.RESOURCE_NAME, sorter.getResourceCriteria())); //$NON-NLS-1$
sortByResource.add(new ToggleSortOrderAction(Policy.bind("ChangeLogModelProvider.9"), ChangeLogModelSorter.PARENT_NAME, ToggleSortOrderAction.RESOURCE_NAME, sorter.getResourceCriteria())); //$NON-NLS-1$
}
/* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.SynchronizePageActionGroup#dispose()
*/
public void dispose() {
sortByComment.dispose();
sortByResource.dispose();
sortByComment.removeAll();
sortByResource.removeAll();
super.dispose();
}
}
/* *****************************************************************************
* Model element for the resources in this layout. They are displayed with filename and path
* onto the same line.
*/
public static class FullPathSyncInfoElement extends SyncInfoModelElement {
public FullPathSyncInfoElement(IDiffContainer parent, SyncInfo info) {
super(parent, info);
}
public String getName() {
IResource resource = getResource();
return resource.getName() + " - " + resource.getFullPath().toString(); //$NON-NLS-1$
}
}
/* *****************************************************************************
* Special sync info that has its kind already calculated.
*/
public class CVSUpdatableSyncInfo extends CVSSyncInfo {
public int kind;
public CVSUpdatableSyncInfo(int kind, IResource local, IResourceVariant base, IResourceVariant remote, Subscriber s) {
super(local, base, remote, s);
this.kind = kind;
}
protected int calculateKind() throws TeamException {
return kind;
}
}
/* *****************************************************************************
* Action group for this layout. It is added and removed for this layout only.
*/
private class FetchLogEntriesJob extends Job {
private Set syncSets = new HashSet();
public FetchLogEntriesJob() {
super(Policy.bind("ChangeLogModelProvider.4")); //$NON-NLS-1$
setUser(false);
}
public boolean belongsTo(Object family) {
return family == ISynchronizeManager.FAMILY_SYNCHRONIZE_OPERATION;
}
public IStatus run(IProgressMonitor monitor) {
if (syncSets != null && !shutdown) {
// Determine the sync sets for which to fetch comment nodes
SyncInfoSet[] updates;
synchronized (syncSets) {
updates = (SyncInfoSet[]) syncSets.toArray(new SyncInfoSet[syncSets.size()]);
syncSets.clear();
}
for (int i = 0; i < updates.length; i++) {
calculateRoots(updates[i], monitor);
}
refreshViewer();
}
return Status.OK_STATUS;
}
public void add(SyncInfoSet set) {
synchronized(syncSets) {
syncSets.add(set);
}
schedule();
}
public boolean shouldRun() {
return !syncSets.isEmpty();
}
};
/* *****************************************************************************
* Descriptor for this model provider
*/
public static class ChangeLogModelProviderDescriptor implements ISynchronizeModelProviderDescriptor {
public static final String ID = TeamUIPlugin.ID + ".modelprovider_cvs_changelog"; //$NON-NLS-1$
public String getId() {
return ID;
}
public String getName() {
return Policy.bind("ChangeLogModelProvider.5"); //$NON-NLS-1$
}
public ImageDescriptor getImageDescriptor() {
return CVSUIPlugin.getPlugin().getImageDescriptor(ICVSUIConstants.IMG_CHANGELOG);
}
};
private static final ChangeLogModelProviderDescriptor descriptor = new ChangeLogModelProviderDescriptor();
public ChangeLogModelProvider(ISynchronizePageConfiguration configuration, SyncInfoSet set, CVSTag tag1, CVSTag tag2) {
super(configuration, set);
this.tag1 = tag1;
this.tag2 = tag2;
configuration.addMenuGroup(ISynchronizePageConfiguration.P_CONTEXT_MENU, SORT_ORDER_GROUP);
this.sortGroup = new ChangeLogActionGroup();
configuration.addActionContribution(sortGroup);
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ui.synchronize.ISynchronizeModelProvider#getDescriptor()
*/
public ISynchronizeModelProviderDescriptor getDescriptor() {
return descriptor;
}
/*
* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.viewers.HierarchicalModelProvider#buildModelObjects(org.eclipse.compare.structuremergeviewer.DiffNode)
*/
protected IDiffElement[] buildModelObjects(ISynchronizeModelElement node) {
if (node == getModelRoot()) {
// Cancel any existing fetching jobs
try {
if (fetchLogEntriesJob != null && fetchLogEntriesJob.getState() != Job.NONE) {
fetchLogEntriesJob.cancel();
fetchLogEntriesJob.join();
}
} catch (InterruptedException e) {
}
// Start building the model from scratch
startUpdateJob(getSyncInfoSet());
}
return new IDiffElement[0];
}
private void startUpdateJob(SyncInfoSet set) {
if(fetchLogEntriesJob == null) {
fetchLogEntriesJob = new FetchLogEntriesJob();
}
fetchLogEntriesJob.add(set);
}
private void refreshViewer() {
UIJob updateUI = new UIJob("") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
BusyIndicator.showWhile(getDisplay(), new Runnable() {
public void run() {
StructuredViewer tree = getViewer();
tree.refresh();
ISynchronizeModelElement root = getModelRoot();
if(root instanceof SynchronizeModelElement)
((SynchronizeModelElement)root).fireChanges();
}
});
return Status.OK_STATUS;
}
};
updateUI.setSystem(true);
updateUI.schedule();
}
private void calculateRoots(SyncInfoSet set, IProgressMonitor monitor) {
try {
monitor.beginTask(null, 100);
// Decide which nodes we have to fetch log histories
SyncInfo[] infos = set.getSyncInfos();
ArrayList commentNodes = new ArrayList();
ArrayList resourceNodes = new ArrayList();
for (int i = 0; i < infos.length; i++) {
SyncInfo info = infos[i];
if(isInterestingChange(info)) {
commentNodes.add(info);
} else {
resourceNodes.add(info);
}
}
// Show elements that don't need their log histories retreived
for (Iterator it = resourceNodes.iterator(); it.hasNext();) {
SyncInfo info = (SyncInfo) it.next();
addNewElementFor(info, null, null);
}
if(! resourceNodes.isEmpty())
refreshViewer();
// Fetch log histories then add elements
SyncInfo[] commentInfos = (SyncInfo[]) commentNodes.toArray(new SyncInfo[commentNodes.size()]);
RemoteLogOperation logs = getSyncInfoComment(commentInfos, Policy.subMonitorFor(monitor, 80));
addLogEntries(commentInfos, logs, Policy.subMonitorFor(monitor, 20));
} catch (CVSException e) {
Utils.handle(e);
} catch (InterruptedException e) {
} finally {
monitor.done();
}
}
/**
* Add the followinf sync info elements to the viewer. It is assumed that these elements have associated
* log entries cached in the log operation.
*/
private void addLogEntries(SyncInfo[] commentInfos, RemoteLogOperation logs, IProgressMonitor monitor) {
try {
monitor.beginTask(null, commentInfos.length * 10);
if (logs != null) {
for (int i = 0; i < commentInfos.length; i++) {
addSyncInfoToCommentNode(commentInfos[i], logs);
monitor.worked(10);
}
// Don't cache log entries when in two way mode.
if (getConfiguration().getComparisonType().equals(ISynchronizePageConfiguration.TWO_WAY)) {
logs.clearEntries();
}
}
} finally {
monitor.done();
}
}
/**
* Create a node for the given sync info object. The logs should contain the log for this info.
*
* @param info the info for which to create a node in the model
* @param log the cvs log for this node
*/
private void addSyncInfoToCommentNode(SyncInfo info, RemoteLogOperation logs) {
ICVSRemoteResource remoteResource = getRemoteResource((CVSSyncInfo)info);
if(tag1 != null && tag2 != null) {
addMultipleRevisions(info, logs, remoteResource);
} else {
addSingleRevision(info, logs, remoteResource);
}
}
/**
* Add multiple log entries to the model.
*
* @param info
* @param logs
* @param remoteResource
*/
private void addMultipleRevisions(SyncInfo info, RemoteLogOperation logs, ICVSRemoteResource remoteResource) {
ILogEntry[] logEntries = logs.getLogEntries(remoteResource);
if(logEntries == null || logEntries.length == 0) {
// If for some reason we don't have a log entry, try the latest
// remote.
addNewElementFor(info, null, null);
} else {
for (int i = 0; i < logEntries.length; i++) {
ILogEntry entry = logEntries[i];
addNewElementFor(info, remoteResource, entry);
}
}
}
/**
* Add a single log entry to the model.
*
* @param info
* @param logs
* @param remoteResource
*/
private void addSingleRevision(SyncInfo info, RemoteLogOperation logs, ICVSRemoteResource remoteResource) {
ILogEntry logEntry = logs.getLogEntry(remoteResource);
// For incoming deletions grab the comment for the latest on the same branch
// which is now in the attic.
try {
String remoteRevision = ((ICVSRemoteFile) remoteResource).getRevision();
if (isDeletedRemotely(info)) {
ILogEntry[] logEntries = logs.getLogEntries(remoteResource);
for (int i = 0; i < logEntries.length; i++) {
ILogEntry entry = logEntries[i];
String revision = entry.getRevision();
if (entry.isDeletion() && ResourceSyncInfo.isLaterRevision(revision, remoteRevision)) {
logEntry = entry;
}
}
}
} catch (TeamException e) {
// continue and skip deletion checks
}
addNewElementFor(info, remoteResource, logEntry);
}
private boolean isDeletedRemotely(SyncInfo info) {
int kind = info.getKind();
if(kind == (SyncInfo.INCOMING | SyncInfo.DELETION)) return true;
if(SyncInfo.getDirection(kind) == SyncInfo.CONFLICTING && info.getRemote() == null) return true;
return false;
}
private void addNewElementFor(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
ISynchronizeModelElement element;
// If the element has a comment then group with common comment
if(remoteResource != null && logEntry != null && isInterestingChange(info)) {
ChangeLogDiffNode changeRoot = getChangeLogDiffNodeFor(logEntry);
if (changeRoot == null) {
changeRoot = new ChangeLogDiffNode(getModelRoot(), logEntry);
addToViewer(changeRoot);
}
if(requiresCustomSyncInfo(info, remoteResource, logEntry)) {
info = new CVSUpdatableSyncInfo(info.getKind(), info.getLocal(), info.getBase(), (RemoteResource)logEntry.getRemoteFile(), ((CVSSyncInfo)info).getSubscriber());
try {
info.init();
} catch (TeamException e) {
// this shouldn't happen, we've provided our own calculate kind
}
}
element = new FullPathSyncInfoElement(changeRoot, info);
} else {
// For nodes without comments, simply parent with the root. These will be outgoing
// additions.
element = new FullPathSyncInfoElement(getModelRoot(), info);
}
addToViewer(element);
}
private boolean requiresCustomSyncInfo(SyncInfo info, ICVSRemoteResource remoteResource, ILogEntry logEntry) {
// Only interested in non-deletions
if (logEntry.isDeletion() || !(info instanceof CVSSyncInfo)) return false;
// Only require a custom sync info if the remote of the sync info
// differs from the remote in the log entry
IResourceVariant remote = info.getRemote();
if (remote == null) return true;
return !remote.equals(remoteResource);
}
/*
* Find an existing comment set
* TODO: we could do better than a linear lookup?
*/
private ChangeLogDiffNode getChangeLogDiffNodeFor(ILogEntry entry) {
IDiffElement[] elements = getModelRoot().getChildren();
for (int i = 0; i < elements.length; i++) {
IDiffElement element = elements[i];
if(element instanceof ChangeLogDiffNode) {
ChangeLogDiffNode other = (ChangeLogDiffNode)element;
ILogEntry thisLog = other.getComment();
if(thisLog.getComment().equals(entry.getComment()) && thisLog.getAuthor().equals(entry.getAuthor())) {
return other;
}
}
}
return null;
}
/*
* Return if this sync info should be considered as part of a commit set.
*/
private boolean isInterestingChange(SyncInfo info) {
int kind = info.getKind();
if(info.getLocal().getType() != IResource.FILE) return false;
if(info.getComparator().isThreeWay()) {
return (kind & SyncInfo.DIRECTION_MASK) != SyncInfo.OUTGOING;
}
return true;
}
/**
* How do we tell which revision has the interesting log message? Use the later
* revision, since it probably has the most up-to-date comment.
*/
private RemoteLogOperation getSyncInfoComment(SyncInfo[] infos, IProgressMonitor monitor) throws CVSException, InterruptedException {
List remotes = new ArrayList();
for (int i = 0; i < infos.length; i++) {
CVSSyncInfo info = (CVSSyncInfo)infos[i];
if (info.getLocal().getType() != IResource.FILE) {
continue;
}
ICVSRemoteResource remote = getRemoteResource(info);
if(remote != null) {
remotes.add(remote);
}
}
ICVSRemoteResource[] remoteResources = (ICVSRemoteResource[]) remotes.toArray(new ICVSRemoteResource[remotes.size()]);
if(logOperation == null) {
logOperation = new RemoteLogOperation(null, remoteResources, tag1, tag2);
}
if(! remotes.isEmpty()) {
logOperation.setRemoteResources(remoteResources);
logOperation.execute(monitor);
}
return logOperation;
}
private ICVSRemoteResource getRemoteResource(CVSSyncInfo info) {
try {
ICVSRemoteResource remote = (ICVSRemoteResource) info.getRemote();
ICVSRemoteResource local = (ICVSRemoteFile) CVSWorkspaceRoot.getRemoteResourceFor(info.getLocal());
if(local == null) {
local = (ICVSRemoteResource)info.getBase();
}
String remoteRevision = getRevisionString(remote);
String localRevision = getRevisionString(local);
boolean useRemote = true;
if (local != null && remote != null) {
useRemote = ResourceSyncInfo.isLaterRevision(remoteRevision, localRevision);
} else if (remote == null) {
useRemote = false;
}
if (useRemote) {
return remote;
} else if (local != null) {
return local;
}
return null;
} catch (CVSException e) {
CVSUIPlugin.log(e);
return null;
}
}
private String getRevisionString(ICVSRemoteResource remoteFile) {
if(remoteFile instanceof RemoteFile) {
return ((RemoteFile)remoteFile).getRevision();
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.views.HierarchicalModelProvider#dispose()
*/
public void dispose() {
shutdown = true;
if(fetchLogEntriesJob != null && fetchLogEntriesJob.getState() != Job.NONE) {
fetchLogEntriesJob.cancel();
}
sortGroup.dispose();
super.dispose();
}
/* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#getViewerSorter()
*/
public ViewerSorter getViewerSorter() {
int commentSort = ChangeLogModelSorter.DATE;
int resourceSort = ChangeLogModelSorter.PATH;
try {
IDialogSettings pageSettings = getConfiguration().getSite().getPageSettings();
if(pageSettings != null) {
commentSort = pageSettings.getInt(P_LAST_COMMENTSORT);
resourceSort = pageSettings.getInt(P_LAST_RESOURCESORT);
}
} catch(NumberFormatException e) {
// ignore and use the defaults.
}
return new ChangeLogModelSorter(commentSort, resourceSort);
}
/* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#doAdd(org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement, org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement)
*/
protected void doAdd(ISynchronizeModelElement parent, ISynchronizeModelElement element) {
AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer();
viewer.add(parent, element);
}
/* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#doRemove(org.eclipse.team.ui.synchronize.viewers.SynchronizeModelElement)
*/
protected void doRemove(ISynchronizeModelElement element) {
AbstractTreeViewer viewer = (AbstractTreeViewer)getViewer();
viewer.remove(element);
}
/* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#handleResourceAdditions(org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent)
*/
protected void handleResourceAdditions(ISyncInfoTreeChangeEvent event) {
startUpdateJob(new SyncInfoSet(event.getAddedResources()));
}
/* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#handleResourceChanges(org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent)
*/
protected void handleResourceChanges(ISyncInfoTreeChangeEvent event) {
// Refresh the viewer for each changed resource
SyncInfo[] infos = event.getChangedResources();
for (int i = 0; i < infos.length; i++) {
SyncInfo info = infos[i];
IResource local = info.getLocal();
removeFromViewer(local);
}
startUpdateJob(new SyncInfoSet(event.getChangedResources()));
}
/* (non-Javadoc)
* @see org.eclipse.team.ui.synchronize.viewers.SynchronizeModelProvider#handleResourceRemovals(org.eclipse.team.core.synchronize.ISyncInfoTreeChangeEvent)
*/
protected void handleResourceRemovals(ISyncInfoTreeChangeEvent event) {
IResource[] removedRoots = event.getRemovedSubtreeRoots();
for (int i = 0; i < removedRoots.length; i++) {
removeFromViewer(removedRoots[i]);
}
// We have to look for folders that may no longer be in the set
// (i.e. are in-sync) but still have descendants in the set
IResource[] removedResources = event.getRemovedResources();
for (int i = 0; i < removedResources.length; i++) {
removeFromViewer(removedResources[i]);
}
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ui.synchronize.SynchronizeModelProvider#removeFromViewer(org.eclipse.core.resources.IResource)
*/
protected void removeFromViewer(IResource resource) {
// First clear the log history cache for the remote element
if (logOperation != null) {
ISynchronizeModelElement element = getModelObject(resource);
if (element instanceof FullPathSyncInfoElement) {
CVSSyncInfo info = (CVSSyncInfo) ((FullPathSyncInfoElement) element).getSyncInfo();
if (info != null) {
ICVSRemoteResource remote = getRemoteResource(info);
logOperation.clearEntriesFor(remote);
}
}
}
// Clear the multiple element cache
if(multipleResourceMap != null) {
List elements = (List)multipleResourceMap.get(resource);
if(elements != null) {
for (Iterator it = elements.iterator(); it.hasNext();) {
ISynchronizeModelElement element = (ISynchronizeModelElement) it.next();
super.removeFromViewer(element);
}
multipleResourceMap.remove(resource);
}
}
// Remove the object now
super.removeFromViewer(resource);
}
/* (non-Javadoc)
* @see org.eclipse.team.internal.ui.synchronize.SynchronizeModelProvider#addToViewer(org.eclipse.team.ui.synchronize.ISynchronizeModelElement)
*/
protected void addToViewer(ISynchronizeModelElement node) {
// Save model elements in our own mapper so that we
// can support multiple elements for the same resource.
IResource r = node.getResource();
if(r != null) {
if(multipleResourceMap == null) {
multipleResourceMap = new HashMap(5);
}
List elements = (List)multipleResourceMap.get(r);
if(elements == null) {
elements = new ArrayList(2);
multipleResourceMap.put(r, elements);
}
elements.add(node);
}
// The super class will do all the interesting work.
super.addToViewer(node);
}
}