blob: 863fcb8d00e9558b765af7fd2a0d289fbd97a6b7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2018 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.team.examples.filesystem.ui;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.List;
import org.eclipse.compare.structuremergeviewer.ICompareInput;
import org.eclipse.core.resources.*;
import org.eclipse.core.resources.mapping.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.action.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.*;
import org.eclipse.team.core.diff.IDiff;
import org.eclipse.team.core.mapping.IMergeContext;
import org.eclipse.team.core.mapping.provider.ResourceDiffTree;
import org.eclipse.team.examples.filesystem.FileSystemPlugin;
import org.eclipse.team.internal.ui.mapping.SynchronizationResourceMappingContext;
import org.eclipse.team.ui.mapping.ISynchronizationCompareAdapter;
import org.eclipse.team.ui.synchronize.ModelMergeOperation;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.Page;
/**
* This class provides the page for the {@link NonSyncMergePart}.
*/
public class NonSyncModelMergePage extends Page {
IMergeContext context;
TreeViewer viewer;
List mappings;
/*
* Content provider that returns the list of conflicting mappings
*/
class PageContentProvider implements ITreeContentProvider {
public Object[] getChildren(Object parentElement) {
if (parentElement instanceof IMergeContext) {
if (mappings == null)
// TODO: should be using a real progress monitor
computeMappings(new NullProgressMonitor());
return mappings.toArray();
}
return new Object[0];
}
public Object getParent(Object element) {
if (element instanceof ResourceMapping) {
return context;
}
return null;
}
public boolean hasChildren(Object element) {
if (element instanceof IMergeContext) {
return true;
}
return false;
}
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
public void dispose() {
// Nothing to do
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
// Nothing to do
}
}
/*
* Label provider that provides a label and image for conflicting resource mappings
*/
class PageLabelProvider extends LabelProvider {
public String getText(Object element) {
if (element instanceof ResourceMapping) {
ResourceMapping mapping = (ResourceMapping) element;
ISynchronizationCompareAdapter adapter = NonSyncMergePart.getCompareAdapter(mapping);
if (adapter != null)
return adapter.getPathString(mapping) + "(" + mapping.getModelProvider().getDescriptor().getLabel() + ")";
}
if (element instanceof ICompareInput) {
ICompareInput ci = (ICompareInput) element;
ci.getName();
}
return super.getText(element);
}
public Image getImage(Object element) {
if (element instanceof ICompareInput) {
ICompareInput ci = (ICompareInput) element;
ci.getImage();
}
if (element instanceof ResourceMapping) {
ResourceMapping mapping = (ResourceMapping) element;
ISynchronizationCompareAdapter adapter = NonSyncMergePart.getCompareAdapter(mapping);
ICompareInput input = adapter.asCompareInput(context, mapping.getModelObject());
if (input != null)
return input.getImage();
}
return super.getImage(element);
}
}
/*
* Sorter that sorts mappings by model and then name
*/
class PageSorter extends ViewerSorter {
public int compare(Viewer viewer, Object e1, Object e2) {
if (e1 instanceof ResourceMapping && e2 instanceof ResourceMapping) {
ResourceMapping m1 = (ResourceMapping) e1;
ResourceMapping m2 = (ResourceMapping) e2;
if (m1.getModelProvider() == m2.getModelProvider()) {
return getLabel(m1).compareTo(getLabel(m2));
}
return compare(m1, m2);
}
return super.compare(viewer, e1, e2);
}
private int compare(ResourceMapping m1, ResourceMapping m2) {
ModelProvider[] sorted = ModelMergeOperation.sortByExtension(new ModelProvider[] { m1.getModelProvider(), m2.getModelProvider() });
return sorted[0] == m1.getModelProvider() ? -1 : 1;
}
private String getLabel(ResourceMapping mapping) {
ISynchronizationCompareAdapter adapter = NonSyncMergePart.getCompareAdapter(mapping);
if (adapter != null)
return adapter.getPathString(mapping);
return "";
}
}
public NonSyncModelMergePage(IMergeContext context) {
super();
this.context = context;
}
/**
* Create the list of all mappings that overlap with the out-of-sync files.
*/
public void computeMappings(IProgressMonitor monitor) {
IModelProviderDescriptor[] descriptors = ModelProvider.getModelProviderDescriptors();
mappings = new ArrayList();
for (int i = 0; i < descriptors.length; i++) {
IModelProviderDescriptor descriptor = descriptors[i];
// Get the subset of files that this model provider cares about
try {
IResource[] resources = descriptor.getMatchingResources(getOutOfSyncFiles());
if (resources.length > 0) {
ModelProvider provider = descriptor.getModelProvider();
// Get the mappings for those resources
ResourceMapping[] mappings = provider.getMappings(resources, new SynchronizationResourceMappingContext(context), monitor);
this.mappings.addAll(Arrays.asList(mappings ));
}
} catch (CoreException e) {
FileSystemPlugin.log(e);
}
}
}
private IResource[] getOutOfSyncFiles() {
IDiff[] diffs = getContext().getDiffTree().getDiffs(ResourcesPlugin.getWorkspace().getRoot(), IResource.DEPTH_INFINITE);
List result = new ArrayList();
for (int i = 0; i < diffs.length; i++) {
IDiff diff = diffs[i];
IResource resource = ResourceDiffTree.getResourceFor(diff);
if (resource.getType() == IResource.FILE)
result.add(resource);
}
return (IResource[]) result.toArray(new IResource[result.size()]);
}
/**
* Return the merge context.
* @return the merge context
*/
public IMergeContext getContext() {
return context;
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.Page#createControl(org.eclipse.swt.widgets.Composite)
*/
public void createControl(Composite parent) {
viewer = new TreeViewer(parent);
viewer.setContentProvider(new PageContentProvider());
viewer.setLabelProvider(new PageLabelProvider());
viewer.setSorter(new PageSorter());
hookContextMenu(viewer);
viewer.setInput(context);
}
/*
* Hook the context menu to display the Overwrite and Mark-as-merged actions
*/
private void hookContextMenu(final TreeViewer viewer) {
final MenuManager menuMgr = new MenuManager();
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(manager -> fillContextMenu(manager));
Menu menu = menuMgr.createContextMenu(viewer.getControl());
viewer.getControl().setMenu(menu);
}
/**
* Fill the context menu.
* @param manager the context menu manager
*/
protected void fillContextMenu(IMenuManager manager) {
/*
* Add a mark as merged action. Because we are not using the
* Synchronization framework and, more specifically, the
* Common Navigator content provider for the model providers,
* we do not have access to the merge handlers of the model.
* Therefore, we are writing are action to detect whether there
* are files that overlap between the selected model elements and
* unselected model elements.
*/
Action markAsMerged = new Action("Mark as Merged") {
public void run() {
try {
final IStructuredSelection selection = viewer.getStructuredSelection();
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(monitor -> {
IDiff[] diffs = getSelectedDiffs(selection, monitor);
if (!checkForModelOverlap(diffs, monitor)) {
return;
}
try {
context.markAsMerged(diffs, false, monitor);
} catch (CoreException e) {
throw new InvocationTargetException(e);
}
});
} catch (InvocationTargetException e) {
FileSystemPlugin.log(new Status(IStatus.ERROR, FileSystemPlugin.ID, 0, e.getTargetException().getMessage(), e.getTargetException()));
} catch (InterruptedException e) {
// Ignore
}
}
};
manager.add(markAsMerged);
}
protected IDiff[] getSelectedDiffs(IStructuredSelection selection, IProgressMonitor monitor) {
Object[] elements = selection.toArray();
return getDiffs(elements, monitor);
}
private IDiff[] getDiffs(Object[] elements, IProgressMonitor monitor) {
Set result = new HashSet();
for (int i = 0; i < elements.length; i++) {
Object element = elements[i];
try {
if (element instanceof ResourceMapping) {
ResourceMapping mapping = (ResourceMapping) element;
ResourceTraversal[] traversals = mapping.getTraversals(new SynchronizationResourceMappingContext(context), monitor);
result.addAll(Arrays.asList(context.getDiffTree().getDiffs(traversals)));
}
} catch (CoreException e) {
FileSystemPlugin.log(e);
}
}
return (IDiff[]) result.toArray(new IDiff[result.size()]);
}
/**
* Check whether any of the diffs overlap with mappings that are not selected
* @param diffs
* @return
*/
protected boolean checkForModelOverlap(IDiff[] diffs, IProgressMonitor monitor) {
// TODO: This check should see if the diffs are also part of mappings
// that are not included in the selection.
return true;
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.Page#getControl()
*/
public Control getControl() {
if (viewer != null)
return viewer.getControl();
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.part.Page#setFocus()
*/
public void setFocus() {
if (viewer != null)
viewer.getControl().setFocus();
}
public ISelectionProvider getViewer() {
return viewer;
}
}