| /******************************************************************************* |
| * 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; |
| } |
| |
| } |