| package org.eclipse.team.internal.ccvs.ui.sync; |
| |
| /* |
| * (c) Copyright IBM Corp. 2000, 2001. |
| * All Rights Reserved. |
| */ |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.compare.structuremergeviewer.Differencer; |
| import org.eclipse.compare.structuremergeviewer.ICompareInput; |
| import org.eclipse.compare.structuremergeviewer.IDiffContainer; |
| import org.eclipse.compare.structuremergeviewer.IDiffElement; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceVisitor; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.preference.IPreferenceStore; |
| import org.eclipse.jface.viewers.Viewer; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.team.core.TeamException; |
| import org.eclipse.team.core.sync.ILocalSyncElement; |
| import org.eclipse.team.core.sync.IRemoteSyncElement; |
| import org.eclipse.team.internal.ccvs.core.CVSException; |
| import org.eclipse.team.internal.ccvs.core.ICVSFile; |
| import org.eclipse.team.internal.ccvs.core.ICVSRunnable; |
| import org.eclipse.team.internal.ccvs.core.client.Session; |
| import org.eclipse.team.internal.ccvs.core.resources.CVSRemoteSyncElement; |
| import org.eclipse.team.internal.ccvs.core.resources.CVSWorkspaceRoot; |
| import org.eclipse.team.internal.ccvs.ui.AvoidableMessageDialog; |
| import org.eclipse.team.internal.ccvs.ui.CVSUIPlugin; |
| import org.eclipse.team.internal.ccvs.ui.ICVSUIConstants; |
| import org.eclipse.team.internal.ccvs.ui.Policy; |
| import org.eclipse.team.internal.ui.sync.CatchupReleaseViewer; |
| import org.eclipse.team.internal.ui.sync.ChangedTeamContainer; |
| import org.eclipse.team.internal.ui.sync.ITeamNode; |
| import org.eclipse.team.internal.ui.sync.SyncCompareInput; |
| import org.eclipse.team.internal.ui.sync.TeamFile; |
| |
| public class CVSSyncCompareInput extends SyncCompareInput { |
| private IResource[] resources; |
| private boolean onlyOutgoing = false; |
| |
| public CVSSyncCompareInput(IResource[] resources) { |
| this(resources, false); |
| } |
| |
| protected CVSSyncCompareInput(IResource[] resources, int granularity) { |
| super(granularity); |
| this.resources = resources; |
| } |
| |
| public CVSSyncCompareInput(IResource[] resources, boolean onlyOutgoing) { |
| super(CVSUIPlugin.getPlugin().getPreferenceStore().getBoolean(ICVSUIConstants.PREF_CONSIDER_CONTENTS) ? ILocalSyncElement.GRANULARITY_CONTENTS : ILocalSyncElement.GRANULARITY_TIMESTAMP); |
| this.onlyOutgoing = onlyOutgoing; |
| this.resources = resources; |
| } |
| |
| /** |
| * Overridden to create a custom DiffTreeViewer in the top left pane of the CompareProvider. |
| * |
| * Subclasses must create and return a new CatchupReleaseViewer, and set the viewer |
| * using setViewer(). |
| */ |
| public Viewer createDiffViewer(Composite parent) { |
| CatchupReleaseViewer catchupReleaseViewer = new CVSCatchupReleaseViewer(parent, this); |
| setViewer(catchupReleaseViewer); |
| // catchupReleaseViewer.getTree().addMouseMoveListener(new MouseMoveListener() { |
| // /** |
| // * @see MouseMoveListener#mouseMove(MouseEvent) |
| // */ |
| // public void mouseMove(MouseEvent e) { |
| // final Tree tree = (Tree)e.widget; |
| // TreeItem item = tree.getItem(new Point(e.x, e.y)); |
| // final TeamFile file; |
| // if (item != null) { |
| // // Hack: this is the only way to get an item from the tree viewer |
| // Object o = item.getData(); |
| // if (o instanceof TeamFile) { |
| // file = (TeamFile)o; |
| // } else file = null; |
| // } else file = null; |
| // |
| // // avoid redundant updates -- identity test is good enough here |
| // if (file == previousTeamFile) return; |
| // previousTeamFile = file; |
| // getShell().getDisplay().asyncExec(new Runnable() { |
| // public void run() { |
| // updateToolTip(tree, file); |
| // } |
| // }); |
| // } |
| // }); |
| return catchupReleaseViewer; |
| } |
| |
| // protected void updateToolTip(Tree tree, TeamFile file) { |
| // String newText = null; |
| // if (file != null && file.getChangeDirection() != ITeamNode.OUTGOING) { |
| // IRemoteSyncElement element = file.getMergeResource().getSyncElement(); |
| // final ICVSRemoteFile remoteFile = (ICVSRemoteFile)element.getRemote(); |
| // final ILogEntry[] logEntry = new ILogEntry[1]; |
| // if (remoteFile != null) { |
| // try { |
| // CVSUIPlugin.runWithProgress(getViewer().getTree().getShell(), true /*cancelable*/, |
| // new IRunnableWithProgress() { |
| // public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { |
| // try { |
| // logEntry[0] = remoteFile.getLogEntry(monitor); |
| // } catch (TeamException ex) { |
| // throw new InvocationTargetException(ex); |
| // } |
| // } |
| // }); |
| // } catch (InterruptedException ex) { |
| // // ignore cancellation |
| // } catch (InvocationTargetException ex) { |
| // // ignore the exception |
| // } |
| // } |
| // if (logEntry[0] != null) { |
| // newText = logEntry[0].getComment(); |
| // } |
| // } |
| // if (tree.isDisposed()) return; |
| // String oldText = tree.getToolTipText(); |
| // if (newText == oldText || newText != null && newText.equals(oldText)) return; |
| // tree.setToolTipText(newText); |
| // } |
| |
| protected IRemoteSyncElement[] createSyncElements(IProgressMonitor monitor) throws TeamException { |
| if (onlyOutgoing) { |
| // Find the outgoing changes in each selected resource |
| final List outgoing = new ArrayList(); |
| try { |
| for (int i = 0; i < resources.length; i++) { |
| resources[i].accept(new IResourceVisitor() { |
| public boolean visit(IResource resource) throws CoreException { |
| // if resource is a file and is dirty, add it to the list |
| if (resource.getType() == IResource.FILE) { |
| if (isDirty((IFile)resource)) { |
| outgoing.add(resource); |
| } |
| } |
| return true; |
| } |
| }); |
| } |
| } catch (CoreException e) { |
| ErrorDialog.openError(getShell(), Policy.bind("simpleInternal"), Policy.bind("internal"), e.getStatus()); //$NON-NLS-1$ //$NON-NLS-2$ |
| CVSUIPlugin.log(e.getStatus()); |
| return new IRemoteSyncElement[0]; |
| } |
| final TeamException[] exception = new TeamException[1]; |
| final IFile[] files = (IFile[])outgoing.toArray(new IFile[outgoing.size()]); |
| final IRemoteSyncElement[] trees = new IRemoteSyncElement[files.length]; |
| Session.run(null, null, true, new ICVSRunnable() { |
| public void run(IProgressMonitor monitor) throws CVSException { |
| int work = 1000 * resources.length; |
| monitor.beginTask(null, work); |
| try { |
| for (int i = 0; i < trees.length; i++) { |
| trees[i] = CVSWorkspaceRoot.getRemoteSyncTree(files[i], null, Policy.subMonitorFor(monitor, 1000)); |
| } |
| } catch (TeamException e) { |
| exception[0] = e; |
| } finally { |
| monitor.done(); |
| } |
| } |
| }, monitor); |
| if (exception[0] != null) throw exception[0]; |
| return trees; |
| } else { |
| IRemoteSyncElement[] trees = new IRemoteSyncElement[resources.length]; |
| int work = 1000 * resources.length; |
| monitor.beginTask(null, work); |
| try { |
| for (int i = 0; i < trees.length; i++) { |
| trees[i] = CVSWorkspaceRoot.getRemoteSyncTree(resources[i], null, Policy.subMonitorFor(monitor, 1000)); |
| } |
| } finally { |
| monitor.done(); |
| } |
| return trees; |
| } |
| } |
| |
| protected void updateView() { |
| // Update the view |
| if (getDiffRoot().hasChildren()) { |
| getViewer().refresh(); |
| } else { |
| getViewer().setInput(null); |
| } |
| |
| // Update the status line |
| updateStatusLine(); |
| } |
| |
| /** |
| * Overridden to mark the source as merged. |
| */ |
| protected void compareInputChanged(ICompareInput source) { |
| super.compareInputChanged(source); |
| updateView(); |
| |
| // prompt user with warning |
| Shell shell = getShell(); |
| if(shell != null) { |
| // prompt |
| if(source instanceof TeamFile) { |
| TeamFile file = (TeamFile)source; |
| int direction = file.getChangeDirection(); |
| int type = file.getChangeType(); |
| if(direction == IRemoteSyncElement.INCOMING || |
| direction == IRemoteSyncElement.CONFLICTING) { |
| promptForConfirmMerge(getShell()); |
| } |
| } |
| } |
| } |
| |
| /* |
| * Helper method to get cvs elements from the selection in the sync editor input |
| */ |
| public static CVSRemoteSyncElement getSyncElementFrom(Object node) { |
| CVSRemoteSyncElement element = null; |
| if (node instanceof TeamFile) { |
| element = (CVSRemoteSyncElement)((TeamFile)node).getMergeResource().getSyncElement(); |
| } else if (node instanceof ChangedTeamContainer) { |
| element = (CVSRemoteSyncElement)((ChangedTeamContainer)node).getMergeResource().getSyncElement(); |
| } |
| return element; |
| } |
| |
| /* |
| * Returns the resources in this input. |
| */ |
| public IResource[] getResources() { |
| return resources; |
| } |
| |
| /* |
| * Inform user that when changes are merged in the sync view that confirm |
| * merge should be called to finish the merge. |
| */ |
| private void promptForConfirmMerge(final Shell shell) { |
| final IPreferenceStore store = CVSUIPlugin.getPlugin().getPreferenceStore(); |
| if(!store.getBoolean(ICVSUIConstants.PREF_PROMPT_ON_SAVING_IN_SYNC)) { |
| return; |
| }; |
| shell.getDisplay().syncExec(new Runnable() { |
| public void run() { |
| AvoidableMessageDialog dialog = new AvoidableMessageDialog( |
| shell, |
| Policy.bind("CVSSyncCompareInput.confirmMergeMessageTitle"), //$NON-NLS-1$ |
| null, // accept the default window icon |
| Policy.bind("CVSSyncCompareInput.confirmMergeMessage"), //$NON-NLS-1$ |
| MessageDialog.INFORMATION, |
| new String[] {IDialogConstants.OK_LABEL}, |
| 0); |
| dialog.open(); |
| if(dialog.isDontShowAgain()) { |
| store.setValue(ICVSUIConstants.PREF_PROMPT_ON_SAVING_IN_SYNC, false); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Wrap the input preparation in a CVS session run so open sessions will be reused and |
| * file contents under the same remote root folder will be fetched using the same connection. |
| * |
| * Also run with refresh prompting if one of the resources is out of sync with the local |
| * file system. |
| */ |
| public Object prepareInput(IProgressMonitor pm) throws InterruptedException, InvocationTargetException { |
| final Object[] result = new Object[] { null }; |
| final Exception[] exception = new Exception[] {null}; |
| try { |
| Session.run(null, null, false, new ICVSRunnable() { |
| public void run(IProgressMonitor monitor) throws CVSException { |
| try { |
| CVSUIPlugin.runWithRefresh(getShell(), resources, new IRunnableWithProgress() { |
| public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { |
| result[0] = CVSSyncCompareInput.super.prepareInput(monitor); |
| } |
| }, monitor); |
| } catch (InterruptedException e) { |
| exception[0] = e; |
| } catch (InvocationTargetException e) { |
| exception[0] = e; |
| } |
| } |
| }, pm); |
| } catch (CVSException e) { |
| throw new InvocationTargetException(e); |
| } |
| |
| if (exception[0] != null) { |
| if (exception[0] instanceof InvocationTargetException) { |
| throw (InvocationTargetException)exception[0]; |
| } else { |
| throw (InterruptedException)exception[0]; |
| } |
| } |
| |
| return result[0]; |
| } |
| |
| /** |
| * Adjust the sync info (to conflicting change) for locally deleted |
| * folders (i.e. outgoing folder deletions) |
| * that have incoming or conflicting changes in one or more children. |
| * |
| * @see MergeAction#removeNodes(ITeamNode[]) |
| */ |
| protected IDiffElement collectResourceChanges(IDiffContainer parent, IRemoteSyncElement tree, IProgressMonitor pm) { |
| IDiffElement element = super.collectResourceChanges(parent, tree, pm); |
| int kind = element.getKind(); |
| if ((element instanceof ChangedTeamContainer) |
| && ((kind & Differencer.CHANGE_TYPE_MASK) == Differencer.DELETION) |
| && ((kind & Differencer.DIRECTION_MASK) == ITeamNode.OUTGOING)) { |
| // Check the children to see if there are any incomming changes |
| if (hasIncomingChanges((ChangedTeamContainer)element)) { |
| ((ChangedTeamContainer)element).setKind(ITeamNode.CONFLICTING | Differencer.CHANGE); |
| } |
| } |
| return element; |
| } |
| |
| private boolean hasIncomingChanges(ChangedTeamContainer container) { |
| IDiffElement[] children = container.getChildren(); |
| for (int i = 0; i < children.length; i++) { |
| IDiffElement element = children[i]; |
| int direction = element.getKind() & Differencer.DIRECTION_MASK; |
| if (direction == ITeamNode.CONFLICTING || direction == ITeamNode.INCOMING) { |
| return true; |
| } |
| if (element instanceof ChangedTeamContainer) { |
| boolean hasIncomingChanges = hasIncomingChanges((ChangedTeamContainer)element); |
| if (hasIncomingChanges) return true; |
| } |
| } |
| return false; |
| } |
| |
| /* |
| * Copied from CVSDecorator |
| */ |
| private static boolean isDirty(ICVSFile cvsFile) { |
| try { |
| // file is dirty or file has been merged by an update |
| if(!cvsFile.isIgnored()) { |
| return cvsFile.isModified(); |
| } else { |
| return false; |
| } |
| } catch (CVSException e) { |
| //if we get an error report it to the log but assume dirty |
| CVSUIPlugin.log(e.getStatus()); |
| return true; |
| } |
| } |
| |
| /* |
| * Copied from CVSDecorator |
| */ |
| private static boolean isDirty(IFile file) { |
| return isDirty(CVSWorkspaceRoot.getCVSFileFor(file)); |
| } |
| } |