Java tutorial
//===================================================================== // //File: $RCSfile: ModelContentMergeViewer.java,v $ //Version: $Revision: 1.11.12.3 $ //Modified: $Date: 2013/07/24 19:20:29 $ // //(c) Copyright 2004-2014 by Mentor Graphics Corp. All rights reserved. // //======================================================================== // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. //======================================================================== package org.xtuml.bp.model.compare.contentmergeviewer; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.UUID; import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.CompareNavigator; import org.eclipse.compare.ICompareNavigator; import org.eclipse.compare.IEditableContent; import org.eclipse.compare.ISharedDocumentAdapter; import org.eclipse.compare.IStreamContentAccessor; import org.eclipse.compare.ITypedElement; import org.eclipse.compare.SharedDocumentAdapter; import org.eclipse.compare.contentmergeviewer.ContentMergeViewer; import org.eclipse.compare.contentmergeviewer.IMergeViewerContentProvider; import org.eclipse.compare.internal.BufferedCanvas; import org.eclipse.compare.internal.CompareMessages; import org.eclipse.compare.internal.CompareUIPlugin; import org.eclipse.compare.internal.ICompareUIConstants; import org.eclipse.compare.internal.MergeViewerContentProvider; import org.eclipse.compare.internal.NavigationEndDialog; import org.eclipse.compare.internal.Utilities; import org.eclipse.compare.structuremergeviewer.Differencer; import org.eclipse.compare.structuremergeviewer.ICompareInput; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.ResourceAttributes; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.ActionContributionItem; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.resource.ColorRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.ITableLabelProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.CTabFolder; import org.eclipse.swt.custom.CTabItem; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.actions.ActionFactory; import org.eclipse.ui.texteditor.IDocumentProvider; import org.xtuml.bp.core.ClassStateMachine_c; import org.xtuml.bp.core.CorePlugin; import org.xtuml.bp.core.InstanceStateMachine_c; import org.xtuml.bp.core.Ooaofooa; import org.xtuml.bp.core.StateMachine_c; import org.xtuml.bp.core.common.ModelRoot; import org.xtuml.bp.core.common.NonRootModelElement; import org.xtuml.bp.core.common.Transaction; import org.xtuml.bp.core.common.TransactionManager; import org.xtuml.bp.core.ui.AbstractModelExportFactory; import org.xtuml.bp.model.compare.ComparableTreeObject; import org.xtuml.bp.model.compare.ComparePlugin; import org.xtuml.bp.model.compare.CompareTransactionManager; import org.xtuml.bp.model.compare.EmptyElement; import org.xtuml.bp.model.compare.ITreeDifferencerProvider; import org.xtuml.bp.model.compare.ModelCacheManager; import org.xtuml.bp.model.compare.ModelCacheManager.ModelLoadException; import org.xtuml.bp.model.compare.ModelMergeProcessor; import org.xtuml.bp.model.compare.TreeDifference; import org.xtuml.bp.model.compare.TreeDifferencer; import org.xtuml.bp.model.compare.actions.CopyDiffAction; import org.xtuml.bp.model.compare.actions.NavigateDownAction; import org.xtuml.bp.model.compare.actions.NavigateUpAction; import org.xtuml.bp.model.compare.providers.ComparableProvider; import org.xtuml.bp.model.compare.providers.ModelCompareContentProvider; import org.xtuml.bp.model.compare.providers.ModelCompareLabelProvider; import org.xtuml.bp.model.compare.providers.NonRootModelElementComparable; import org.xtuml.bp.model.compare.structuremergeviewer.ModelStructureDiffViewer; import org.xtuml.bp.ui.canvas.CanvasPlugin; import org.xtuml.bp.ui.canvas.Connector_c; import org.xtuml.bp.ui.canvas.ElementSpecification_c; import org.xtuml.bp.ui.canvas.Graphconnector_c; import org.xtuml.bp.ui.canvas.Graphedge_c; import org.xtuml.bp.ui.canvas.GraphicalElement_c; public class ModelContentMergeViewer extends ContentMergeViewer implements IModelContentMergeViewer, IResourceChangeListener { private static final String BUNDLE_NAME = "org.xtuml.bp.model.compare.ComparePluginMessages"; //$NON-NLS-1$; // width for center area public static final int CENTER_WIDTH = 42; private static HashMap<Object, ModelContentMergeViewer> instanceMap = new HashMap<Object, ModelContentMergeViewer>(); // model cache manager ModelCacheManager modelManager = ComparePlugin.getDefault().getModelCacheManager(); private SynchronizedTreeViewer leftTreeViewer; private SynchronizedTreeViewer rightTreeViewer; private Canvas canvas; private TreeDifferencer differencer; private SynchronizedTreeViewer ancestorTreeViewer; private double[] basicCenterCurve; private CompareConfiguration configuration; private CompareTransactionManager compareTransactionManager; private ActionContributionItem nextDifference; private ActionContributionItem previousDifference; // the following symbolic constants must match the IDs in Compare's // plugin.xml private static final String INCOMING_COLOR = "INCOMING_COLOR"; //$NON-NLS-1$ private static final String OUTGOING_COLOR = "OUTGOING_COLOR"; //$NON-NLS-1$ private static final String CONFLICTING_COLOR = "CONFLICTING_COLOR"; //$NON-NLS-1$ private static final String RESOLVED_COLOR = "RESOLVED_COLOR"; //$NON-NLS-1$ private static final String CONTAINED_COLOR = "CONTAINED_COLOR"; //$NON-NLS-1$ private RGB INCOMING_BASE; private RGB INCOMING; private RGB CONFLICT_BASE; private RGB CONFLICT; private RGB OUTGOING_BASE; private RGB OUTGOING; private RGB RESOLVED; private RGB CONTAINED; private HashMap<RGB, Color> colors; private Action undo; private Action redo; protected boolean copySelection; public boolean mergeSupportEnabled = true; protected boolean ignoreNextEvent = false; private Object oldInput; private CTabFolder leftFolder; private CTabFolder rightFolder; private List<ModelMergeViewer> leftExtensions = new ArrayList<ModelMergeViewer>(); private List<ModelMergeViewer> rightExtensions = new ArrayList<ModelMergeViewer>(); private Map<NonRootModelElement, ICompareInput> savedModels = new HashMap<NonRootModelElement, ICompareInput>(); private Map<NonRootModelElement, ICompareInput> visitedModels = new HashMap<NonRootModelElement, ICompareInput>(); // This field will enable graphical data, and in the future maybe other // data, when set to true. This is helpful currently when looking into // graphical related changes as they are used during merge but not shown // in the tree public boolean debug = false; public ModelContentMergeViewer(Composite parent, CompareConfiguration configuration) { super(SWT.NONE, ResourceBundle.getBundle(BUNDLE_NAME), configuration); this.configuration = configuration; buildControl(parent); // undo and redo undo = getCompareTransactionManager().getUndoAction(); redo = getCompareTransactionManager().getRedoAction(); if (configuration.getContainer().getActionBars() != null) { configuration.getContainer().getActionBars().setGlobalActionHandler(ActionFactory.UNDO.getId(), undo); configuration.getContainer().getActionBars().setGlobalActionHandler(ActionFactory.REDO.getId(), redo); } setContentProvider(new MergeViewerContentProvider(configuration) { @Override public void saveLeftContent(Object element, byte[] bytes) { if (element instanceof ICompareInput) { ICompareInput node = (ICompareInput) element; updateSMIds(node, true); writeData(node, true); NonRootModelElement[] rootElements = new NonRootModelElement[0]; try { rootElements = modelManager.getRootElements(element, this, false, getLeftCompareRoot(), ModelCacheManager.getLeftKey(element)); if (rootElements.length != 0) { ICompareInput savedInput = savedModels.get(rootElements[0]); if (savedInput == null) { savedModels.put(rootElements[0], node); } } } catch (ModelLoadException e) { CorePlugin.logError("Unable to get root model elements from compare input.", e); } finally { modelManager.releaseModel(element, this, ModelCacheManager.getLeftKey(element)); } } } @Override public void saveRightContent(Object element, byte[] bytes) { if (element instanceof ICompareInput) { ICompareInput node = (ICompareInput) element; updateSMIds(node, false); writeData(node, false); NonRootModelElement[] rootElements = new NonRootModelElement[0]; try { rootElements = modelManager.getRootElements(element, this, false, getRightCompareRoot(), ModelCacheManager.getRightKey(element)); if (rootElements.length != 0) { ICompareInput savedInput = savedModels.get(rootElements[0]); if (savedInput == null) { savedModels.put(rootElements[0], node); } } } catch (ModelLoadException e) { CorePlugin.logError("Unable to get root model elements from compare input.", e); } finally { modelManager.releaseModel(element, this, ModelCacheManager.getRightKey(element)); } } } }); ResourcesPlugin.getWorkspace().addResourceChangeListener(this); } protected void updateSMIds(ICompareInput node, boolean left) { // reset any SM ids that were upgrade for compare/merge Object key = ModelCacheManager.getLeftKey(node); if (!left) { key = ModelCacheManager.getRightKey(node); } Ooaofooa compareRoot = getLeftCompareRoot(); if (!left) { compareRoot = getRightCompareRoot(); } UUID originalSMId = modelManager.getOriginalSMIdFromEntry(key); if (originalSMId != null) { NonRootModelElement[] rootElements; try { rootElements = modelManager.getRootElements(node, this, false, compareRoot, key); // only support case where there is only one root StateMachine_c sm = null; if (rootElements[0] instanceof InstanceStateMachine_c) { sm = StateMachine_c.getOneSM_SMOnR517((InstanceStateMachine_c) rootElements[0]); } if (rootElements[0] instanceof ClassStateMachine_c) { sm = StateMachine_c.getOneSM_SMOnR517((ClassStateMachine_c) rootElements[0]); } if (sm != null) { ModelCacheManager.updateIdForStateMachine(originalSMId, sm); } } catch (ModelLoadException e) { CorePlugin.logError("Unable to load compare data.", e); } } } protected void writeData(ICompareInput input, boolean toLeft) { try { ITypedElement destination = input.getLeft(); Ooaofooa root = Ooaofooa.getInstance(ModelRoot.getLeftCompareRootPrefix() + input.hashCode()); if (!toLeft) { destination = input.getRight(); root = Ooaofooa.getInstance(ModelRoot.getRightCompareRootPrefix() + input.hashCode()); } final NonRootModelElement rootElement = modelManager.getRootElements(destination, null, false, root, ModelCacheManager.getLeftKey(input))[0]; if (destination instanceof IEditableContent) { // before saving copy all graphical changes that are // non-conflicting List<TreeDifference> incomingGraphicalDifferences = getIncomingGraphicalDifferences(toLeft); if (incomingGraphicalDifferences.size() > 0) { mergeIncomingGraphicalChanges(incomingGraphicalDifferences, toLeft, input); } ByteArrayOutputStream baos = new ByteArrayOutputStream(); AbstractModelExportFactory modelExportFactory = CorePlugin.getModelExportFactory(); IRunnableWithProgress runnable = modelExportFactory.create(root, baos, rootElement); runnable.run(new NullProgressMonitor()); ((IEditableContent) destination).setContent(baos.toByteArray()); if (destination instanceof LocalResourceTypedElement) { ((IFile) ((LocalResourceTypedElement) destination).getResource()).setContents( new ByteArrayInputStream(baos.toByteArray()), IFile.FORCE | IFile.KEEP_HISTORY, new NullProgressMonitor()); } WorkspaceJob job = new WorkspaceJob("Refresh workspace content") { @Override public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { NonRootModelElement elementGlobally = (NonRootModelElement) Ooaofooa.getDefaultInstance() .getInstanceList(rootElement.getClass()).getGlobal(rootElement.getInstanceKey()); if (elementGlobally != null) { if (elementGlobally.getFile() != null) { elementGlobally.getFile().refreshLocal(IFile.DEPTH_INFINITE, monitor); } } return Status.OK_STATUS; } }; job.schedule(1500); } } catch (FileNotFoundException e) { ComparePlugin.writeToLog("Unable to save merge data.", e, ModelContentMergeViewer.class); } catch (ModelLoadException e) { ComparePlugin.writeToLog("Unable to save merge data.", e, ModelContentMergeViewer.class); } catch (InvocationTargetException e) { ComparePlugin.writeToLog("Unable to save merge data.", e, ModelContentMergeViewer.class); } catch (InterruptedException e) { ComparePlugin.writeToLog("Unable to save merge data.", e, ModelContentMergeViewer.class); } catch (CoreException e) { ComparePlugin.writeToLog("Unable to save merge data.", e, ModelContentMergeViewer.class); } } protected void mergeIncomingGraphicalChanges(List<TreeDifference> incomingGraphicalDifferences, boolean left, ICompareInput input) { ITreeDifferencerProvider provider = (ITreeDifferencerProvider) leftTreeViewer.getContentProvider(); ITableLabelProvider labelProvider = (ITableLabelProvider) leftTreeViewer.getLabelProvider(); Ooaofooa destinationRoot = Ooaofooa.getInstance(ModelRoot.getLeftCompareRootPrefix() + input.hashCode()); if (!left) { provider = (ITreeDifferencerProvider) rightTreeViewer.getContentProvider(); labelProvider = (ITableLabelProvider) rightTreeViewer.getLabelProvider(); destinationRoot = Ooaofooa.getInstance(ModelRoot.getRightCompareRootPrefix() + input.hashCode()); } for (TreeDifference difference : incomingGraphicalDifferences) { try { ModelMergeProcessor.merge(differencer, difference, left, provider, labelProvider, destinationRoot); } catch (IOException e) { CorePlugin.logError("Unable to automatically merge graphical changes", e); } } } @Override protected void createToolItems(ToolBarManager toolBarManager) { CompareConfiguration cc = getCompareConfiguration(); if (cc.isRightEditable()) { final CopyDiffAction a = new CopyDiffAction(this, true); leftTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { boolean result = a.isEnabled(); a.setEnabled(!result); a.setEnabled(result); } }); rightTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { boolean result = a.isEnabled(); a.setEnabled(!result); a.setEnabled(result); } }); Utilities.initAction(a, getResourceBundle(), "action.CopyDiffLeftToRight."); //$NON-NLS-1$ ActionContributionItem item = new ActionContributionItem(a); item.setVisible(true); toolBarManager.appendToGroup("merge", item); //$NON-NLS-1$ } if (cc.isLeftEditable()) { final CopyDiffAction a = new CopyDiffAction(this, false); leftTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { boolean result = a.isEnabled(); a.setEnabled(!result); a.setEnabled(result); } }); rightTreeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { boolean result = a.isEnabled(); a.setEnabled(!result); a.setEnabled(result); } }); Utilities.initAction(a, getResourceBundle(), "action.CopyDiffRightToLeft."); //$NON-NLS-1$ ActionContributionItem item = new ActionContributionItem(a); item.setVisible(true); toolBarManager.appendToGroup("merge", item); //$NON-NLS-1$ } Action navigateDown = new NavigateDownAction(this); Utilities.initAction(navigateDown, getResourceBundle(), "action.NextDiff."); nextDifference = new ActionContributionItem(navigateDown); toolBarManager.appendToGroup("navigation", nextDifference); //$NON-NLS-1$ Action navigateUp = new NavigateUpAction(this); Utilities.initAction(navigateUp, getResourceBundle(), "action.PrevDiff."); previousDifference = new ActionContributionItem(navigateUp); toolBarManager.appendToGroup("navigation", previousDifference); //$NON-NLS-1$ } public TreeDifference getNextDifference(boolean down) { TreeDifference nextDifference = null; if (down) { nextDifference = locateNextDifferenceBelow(); } else { nextDifference = locateNextDifferenceAbove(); } return nextDifference; } private TreeDifference locateNextDifferenceBelow() { TreeItem[] selection = leftTreeViewer.getTree().getSelection(); // if the selection is empty check the right side as we // may have an item selected which matches a missing // item on the left TreeItem selected = null; if (selection.length == 0) { selection = rightTreeViewer.getTree().getSelection(); if (selection.length != 0) { selected = selection[selection.length - 1]; } } else { // otherwise we use the last selected item selected = selection[selection.length - 1]; } // if the selection is empty return the first // difference if (selected == null) { return differencer.getNextDifference(null); } else { // walk the tree down return walkDown(selected.getData()); } } private TreeDifference walkDown(Object selected) { return scanChildrenForDifference(selected, null, true, true); } private Object[] getChildrenIncludingMissing(Object parent) { if (parent instanceof NonRootModelElementComparable) { NonRootModelElementComparable nrmec = (NonRootModelElementComparable) parent; NonRootModelElement nrme = (NonRootModelElement) nrmec.getRealElement(); if (nrme.getModelRoot().getId().startsWith(ModelRoot.getRightCompareRootPrefix())) { TreeItem matchingItem = SynchronizedTreeViewer.getMatchingItem(nrmec, leftTreeViewer); if (matchingItem != null) { parent = matchingItem.getData(); } } } ITreeContentProvider modelContentProvider = (ITreeContentProvider) leftTreeViewer.getContentProvider(); Object[] children = modelContentProvider.getChildren(parent); List<Object> orderedChildren = new ArrayList<Object>(); orderedChildren.addAll(Arrays.asList(children)); // add missing children in List<TreeDifference> missingDiffs = differencer.getDifferences(parent, true); int processed = 0; int add = 1; for (TreeDifference difference : missingDiffs) { if (difference.getElement() == null) { if (processed == difference.getLocation()) { add++; } else { add = 1; } orderedChildren.add(difference.getLocation() + add, difference.getMatchingDifference().getElement()); processed = difference.getLocation(); } } return orderedChildren.toArray(); } private TreeDifference scanChildrenForDifference(Object parent, Object selected, boolean down, boolean scanParent) { Object[] children = getChildrenIncludingMissing(parent); List<Object> reversable = Arrays.asList(children); if (!down) { // reverse the list Collections.reverse(reversable); } boolean checkChild = selected == null; for (Object child : reversable) { if (checkChild) { List<TreeDifference> differences = differencer.getDifferences(child, true); if (differences.isEmpty()) { // try the right map differences = differencer.getDifferences(child, false); } for (TreeDifference difference : differences) { if (difference.getElement() != null) { return difference; } } TreeDifference difference = scanChildrenForDifference(child, null, down, false); if (difference != null) { return difference; } } if (child.equals(selected)) { checkChild = true; } } if (scanParent) { Object nextParent = ((ITreeContentProvider) leftTreeViewer.getContentProvider()).getParent(parent); if (nextParent == null) { return null; } return scanChildrenForDifference(nextParent, parent, down, true); } else { return null; } } private TreeDifference locateNextDifferenceAbove() { TreeItem[] selection = leftTreeViewer.getTree().getSelection(); // if the selection is empty check the right side as we // may have an item selected which matches a missing // item on the left TreeItem selected = null; if (selection.length == 0) { selection = rightTreeViewer.getTree().getSelection(); if (selection.length != 0) { selected = selection[0]; } } else { // otherwise we use the first selected item selected = selection[0]; } // if the selection is empty return null if (selected == null) { return null; } else { // walk the tree up return walkUp(selected.getData()); } } private TreeDifference walkUp(Object selected) { return scanChildrenForDifference(selected, null, false, true); } public void endOfDocumentReached(boolean down) { Control c = getControl(); if (Utilities.okToUse(c)) { handleEndOfDocumentReached(c.getShell(), down); } } private void handleEndOfDocumentReached(Shell shell, boolean next) { IPreferenceStore store = CompareUIPlugin.getDefault().getPreferenceStore(); String value = store.getString(ICompareUIConstants.PREF_NAVIGATION_END_ACTION); if (!value.equals(ICompareUIConstants.PREF_VALUE_PROMPT)) { performEndOfDocumentAction(shell, store, ICompareUIConstants.PREF_NAVIGATION_END_ACTION, next); } else { shell.getDisplay().beep(); String loopMessage; String nextMessage; String message; String title; if (next) { title = CompareMessages.TextMergeViewer_0; message = CompareMessages.TextMergeViewer_1; loopMessage = CompareMessages.TextMergeViewer_2; nextMessage = CompareMessages.TextMergeViewer_3; } else { title = CompareMessages.TextMergeViewer_4; message = CompareMessages.TextMergeViewer_5; loopMessage = CompareMessages.TextMergeViewer_6; nextMessage = CompareMessages.TextMergeViewer_7; } String[] localLoopOption = new String[] { loopMessage, ICompareUIConstants.PREF_VALUE_LOOP }; String[] nextElementOption = new String[] { nextMessage, ICompareUIConstants.PREF_VALUE_NEXT }; String[] doNothingOption = new String[] { CompareMessages.TextMergeViewer_17, ICompareUIConstants.PREF_VALUE_DO_NOTHING }; NavigationEndDialog dialog = new NavigationEndDialog(shell, title, null, message, new String[][] { localLoopOption, nextElementOption, doNothingOption }); int result = dialog.open(); if (result == Window.OK) { performEndOfDocumentAction(shell, store, ICompareUIConstants.PREF_NAVIGATION_END_ACTION_LOCAL, next); if (dialog.getToggleState()) { String oldValue = store.getString(ICompareUIConstants.PREF_NAVIGATION_END_ACTION); store.putValue(ICompareUIConstants.PREF_NAVIGATION_END_ACTION, store.getString(ICompareUIConstants.PREF_NAVIGATION_END_ACTION_LOCAL)); store.firePropertyChangeEvent(ICompareUIConstants.PREF_NAVIGATION_END_ACTION, oldValue, store.getString(ICompareUIConstants.PREF_NAVIGATION_END_ACTION_LOCAL)); } } } } private void performEndOfDocumentAction(Shell shell, IPreferenceStore store, String key, boolean next) { String value = store.getString(key); if (value.equals(ICompareUIConstants.PREF_VALUE_DO_NOTHING)) { return; } if (value.equals(ICompareUIConstants.PREF_VALUE_NEXT)) { ICompareNavigator navigator = getCompareConfiguration().getContainer().getNavigator(); if (hasNextElement(next)) { navigator.selectChange(next); } } else { if (next) { revealAndSelectDifference(differencer.getNextDifference(null)); } else { revealAndSelectDifference(differencer.getLastDifference()); } } } private boolean hasNextElement(boolean down) { ICompareNavigator navigator = getCompareConfiguration().getContainer().getNavigator(); if (navigator instanceof CompareNavigator) { CompareNavigator n = (CompareNavigator) navigator; return n.hasChange(down); } return false; } public void revealAndSelectDifference(TreeDifference next) { if (next == null) { // no differences, just return return; } leftTreeViewer.getTree().deselectAll(); rightTreeViewer.getTree().deselectAll(); if (!differencer.getLeftDifferences().contains(next)) { next = next.getMatchingDifference(); } if (next.getElement() != null) { leftTreeViewer.setSelection(new StructuredSelection(next.getElement()), true); TreeItem matching = SynchronizedTreeViewer.getMatchingItem(next.getElement(), leftTreeViewer); if (matching != null) { leftTreeViewer.getTree().setTopItem(matching); } Object right = next.getMatchingDifference().getElement(); if (right != null) { rightTreeViewer.setSelection(new StructuredSelection(right), true); TreeItem rightItem = SynchronizedTreeViewer.getMatchingItem(right, rightTreeViewer); if (rightItem != null) { rightTreeViewer.getTree().setTopItem(rightItem); } } else { TreeItem rightParentItem = SynchronizedTreeViewer .getMatchingItem(next.getMatchingDifference().getParent(), rightTreeViewer); if (rightParentItem != null) { rightTreeViewer.reveal(rightParentItem.getData()); rightTreeViewer.setExpandedState(rightParentItem.getData(), true); } } } else { if (next.getParent() != null) { TreeItem leftParentItem = SynchronizedTreeViewer.getMatchingItem(next.getParent(), leftTreeViewer); if (leftParentItem != null) { //leftTreeViewer.reveal(leftParentItem.getData()); leftTreeViewer.setExpandedState(leftParentItem.getData(), true); } } rightTreeViewer.setSelection(new StructuredSelection(next.getMatchingDifference().getElement()), true); if (next.getMatchingDifference().getElement() != null) { TreeItem matchingItem = SynchronizedTreeViewer .getMatchingItem(next.getMatchingDifference().getElement(), rightTreeViewer); if (matchingItem != null) { rightTreeViewer.getTree().setTopItem(matchingItem); } } } refreshCenter(); } @Override protected void handleCompareInputChange() { setLeftDirty(false); setRightDirty(false); super.handleCompareInputChange(); } public CompareConfiguration getConfiguration() { return configuration; } @Override public void copy(boolean leftToRight) { if (!mergeSupportEnabled) { return; } Ooaofooa modelRoot = getLeftCompareRoot(); if (leftToRight) { modelRoot = getRightCompareRoot(); } List<TreeDifference> leftDifferences = getSelectedDifferences(true, !copySelection); List<TreeDifference> rightDifferences = getSelectedDifferences(false, !copySelection); List<TreeDifference> differences = new ArrayList<TreeDifference>(); // must copy over any removals as well, but there is nothing to select // in that case so we must get the difference from the other side if (leftToRight) { if (copySelection) { differences.addAll(leftDifferences); for (TreeDifference difference : rightDifferences) { if (difference.getMatchingDifference().getElement() == null) { differences.add(difference.getMatchingDifference()); } } } } else { if (copySelection) { differences.addAll(rightDifferences); for (TreeDifference difference : leftDifferences) { if (difference.getMatchingDifference().getElement() == null) { differences.add(difference.getMatchingDifference()); } } } } if (!copySelection) { List<TreeDifference> allDiffs = new ArrayList<TreeDifference>(); if (leftToRight) allDiffs.addAll(leftDifferences); else allDiffs.addAll(rightDifferences); // add only the correct change boolean onlyOutgoingChanges = true; for (TreeDifference difference : rightDifferences) { if ((difference.getKind() & Differencer.DIRECTION_MASK) == Differencer.RIGHT || (difference.getKind() & Differencer.DIRECTION_MASK) == Differencer.CONFLICTING) { onlyOutgoingChanges = false; break; } } for (TreeDifference diff : allDiffs) { if (onlyOutgoingChanges) { // allow copy non-conflicting changes to overwrite // left changes differences.add(diff); } else { if ((diff.getKind() & Differencer.DIRECTION_MASK) == Differencer.LEFT && leftToRight) { differences.add(diff); } if ((diff.getKind() & Differencer.DIRECTION_MASK) == Differencer.RIGHT && !leftToRight) { differences.add(diff); } } } } List<TreeDifference> mergeDifferences = new ArrayList<TreeDifference>(); // remove any contained differences and if the // container difference is not selected add it removeContainerDifferences(differences, mergeDifferences); Transaction transaction = compareTransactionManager.startCompareTransaction(); try { SynchronizedTreeViewer viewer = leftTreeViewer; if (leftToRight) { viewer = rightTreeViewer; } // sort the differences, making sure that all additions are first // otherwise ordering differences may not be handled appropriately // when using the copy all action List<TreeDifference> additionsOrRemovals = new ArrayList<TreeDifference>(); List<TreeDifference> remainder = new ArrayList<TreeDifference>(); for (TreeDifference difference : mergeDifferences) { if (difference.getElement() != null && difference.getMatchingDifference().getElement() != null) { remainder.add(difference); } else { additionsOrRemovals.add(difference); } } mergeDifferences.clear(); mergeDifferences.addAll(additionsOrRemovals); mergeDifferences.addAll(remainder); for (TreeDifference difference : mergeDifferences) { // skip graphical data at this point if (SynchronizedTreeViewer.differenceIsGraphical(difference) && !debug) { continue; } if ((difference.getKind() & Differencer.DIRECTION_MASK) == Differencer.CONFLICTING && !copySelection) { continue; } boolean changed = ModelMergeProcessor.merge(differencer, difference, !leftToRight, (ITreeDifferencerProvider) viewer.getContentProvider(), (ITableLabelProvider) viewer.getLabelProvider(), modelRoot); if (changed) { if (leftToRight) { markRightDirty(true); } else { markLeftDirty(true); } } } compareTransactionManager.endTransaction(transaction); } catch (Exception e) { // need to cancel transaction if (compareTransactionManager.getActiveTransaction() != null && transaction != null) { compareTransactionManager.cancelTransaction(transaction); } ComparePlugin.writeToLog("Unable to complete merge transaction", e, getClass()); } } public List<TreeDifference> getSelectedDifferences(boolean left, boolean ignoreSelection) { if (ignoreSelection && left) { return differencer.getLeftDifferences(); } if (ignoreSelection && !left) { return differencer.getRightDifferences(); } SynchronizedTreeViewer viewer = leftTreeViewer; List<TreeDifference> differences = new ArrayList<TreeDifference>(); if (!left) { viewer = rightTreeViewer; } IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); for (Iterator<?> iterator = selection.iterator(); iterator.hasNext();) { Object next = iterator.next(); List<TreeDifference> located = differencer.getDifferences(next, left); if (located.isEmpty()) { // walk the hierarchy up to see if we find a difference that // includes children Object parent = ((ITreeContentProvider) viewer.getContentProvider()).getParent(next); while (parent != null) { located = differencer.getDifferences(parent, left); if (!located.isEmpty()) { if (located.size() == 1) { if (located.get(0).getIncludeChildren() && located.get(0).getElement() != null) { differences.addAll(located); break; } else { parent = ((ITreeContentProvider) viewer.getContentProvider()).getParent(parent); } } else { // do not continue break; } } else { parent = ((ITreeContentProvider) viewer.getContentProvider()).getParent(parent); } } // next see if this is a non-expanded parent that contains // differences and include all of them differences.addAll(SynchronizedTreeViewer.scanChildrenForDifferences(next, differencer, (ITreeContentProvider) viewer.getContentProvider(), viewer == getLeftViewer())); } else { differences.addAll(located); } } return differences; } protected void createControls(Composite composite) { leftExtensions.addAll(getExtensions()); leftFolder = new CTabFolder(composite, SWT.BOTTOM); leftFolder.setBorderVisible(false); if (leftExtensions.size() == 0) { leftFolder.setSingle(true); leftFolder.setTabHeight(0); leftFolder.marginHeight = -2; leftFolder.marginWidth = -2; } CTabItem leftItem = new CTabItem(leftFolder, SWT.NONE); Composite leftPanel = new Composite(leftFolder, SWT.NONE); leftPanel.setLayout(new FillLayout()); leftPanel.setFont(composite.getFont()); leftTreeViewer = new SynchronizedTreeViewer(leftPanel, SWT.H_SCROLL | SWT.MULTI | SWT.FULL_SELECTION | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.BORDER, this, configuration.isLeftEditable(), false); ModelCompareContentProvider leftContentProvider = new ModelCompareContentProvider(); leftContentProvider.setIncludeNonTreeData(debug); leftTreeViewer.setContentProvider(leftContentProvider); leftTreeViewer.setUseHashlookup(true); leftTreeViewer.setLabelProvider(new ModelCompareLabelProvider()); leftItem.setControl(leftPanel); leftItem.setText("Tree"); for (ModelMergeViewer viewerExtension : leftExtensions) { CTabItem item = new CTabItem(leftFolder, SWT.NONE); Composite panel = new Composite(leftFolder, SWT.NONE); panel.setLayout(new FillLayout()); panel.setFont(composite.getFont()); viewerExtension.createControl(panel); viewerExtension.setType(ModelMergeViewer.LEFT); item.setControl(panel); item.setText(viewerExtension.getTitle()); } leftFolder.setSelection(leftItem); rightExtensions.addAll(getExtensions()); rightFolder = new CTabFolder(composite, SWT.BOTTOM); rightFolder.setBorderVisible(false); rightFolder.setSimple(true); if (rightExtensions.size() == 0) { rightFolder.setSingle(true); rightFolder.setTabHeight(0); rightFolder.marginHeight = -2; rightFolder.marginWidth = -2; } CTabItem rightItem = new CTabItem(rightFolder, SWT.NONE); Composite rightPanel = new Composite(rightFolder, SWT.NONE); rightPanel.setLayout(new FillLayout()); rightPanel.setFont(composite.getFont()); rightTreeViewer = new SynchronizedTreeViewer(rightPanel, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.FULL_SELECTION | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.BORDER, this, configuration.isRightEditable(), false); rightTreeViewer.addSynchronizationViewer(leftTreeViewer); rightTreeViewer.setUseHashlookup(true); leftTreeViewer.addSynchronizationViewer(rightTreeViewer); ModelCompareContentProvider rightProvider = new ModelCompareContentProvider(); rightProvider.setIncludeNonTreeData(debug); rightTreeViewer.setContentProvider(rightProvider); rightTreeViewer.setLabelProvider(new ModelCompareLabelProvider()); rightItem.setControl(rightPanel); rightItem.setText("Tree"); for (ModelMergeViewer viewerExtension : rightExtensions) { CTabItem item = new CTabItem(rightFolder, SWT.NONE); Composite panel = new Composite(rightFolder, SWT.NONE); panel.setLayout(new FillLayout()); panel.setFont(composite.getFont()); viewerExtension.createControl(panel); viewerExtension.setType(ModelMergeViewer.RIGHT); item.setControl(panel); item.setText(viewerExtension.getTitle()); } rightFolder.setSelection(rightItem); ancestorTreeViewer = new SynchronizedTreeViewer(composite, SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.FULL_SELECTION | SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND | SWT.BORDER, this, false, true); ancestorTreeViewer.setUseHashlookup(true); ModelCompareContentProvider ancestorProvider = new ModelCompareContentProvider(); ancestorProvider.setIncludeNonTreeData(debug); ancestorTreeViewer.setContentProvider(ancestorProvider); ancestorTreeViewer.setLabelProvider(new ModelCompareLabelProvider()); leftTreeViewer.addSynchronizationViewer(ancestorTreeViewer); rightTreeViewer.addSynchronizationViewer(ancestorTreeViewer); ancestorTreeViewer.addSynchronizationViewer(leftTreeViewer); ancestorTreeViewer.addSynchronizationViewer(rightTreeViewer); } private Collection<? extends ModelMergeViewer> getExtensions() { List<ModelMergeViewer> viewers = new ArrayList<ModelMergeViewer>(); IExtensionRegistry reg = Platform.getExtensionRegistry(); IExtensionPoint extPt = reg.getExtensionPoint("org.xtuml.bp.model.compare.compareDifferencers"); //$NON-NLS-1$ IExtension[] extensions = extPt.getExtensions(); for (int i = 0; i < extensions.length; i++) { IConfigurationElement[] configurationElements = extensions[i].getConfigurationElements(); for (int j = 0; j < configurationElements.length; j++) { if (configurationElements[j].getName().equals("differenceEngine")) { //$NON-NLS-1$ try { Object viewer = configurationElements[j].createExecutableExtension("differenceViewer"); //$NON-NLS-1$ if (viewer != null) { viewers.add((ModelMergeViewer) viewer); } } catch (CoreException e) { CanvasPlugin.logError("Unable to create executable extension from point.", e); //$NON-NLS-1$ } } } } return viewers; } @Override protected byte[] getContents(boolean left) { return null; } @Override protected void handleResizeAncestor(int x, int y, int width, int height) { ancestorTreeViewer.getControl().setBounds(x, y, width, height); } @Override protected void handleResizeLeftRight(int x, int y, int leftWidth, int centerWidth, int rightWidth, int height) { // always split left and right width int totalWidth = leftWidth + rightWidth; leftWidth = totalWidth / 2; rightWidth = totalWidth - leftWidth; if (getCenterPart() != null) { getCenterPart().setBounds(leftWidth - CENTER_WIDTH / 2, y, CENTER_WIDTH, height); } leftFolder.setBounds(x, y, leftWidth - CENTER_WIDTH / 2, height); rightFolder.setBounds(x + leftWidth + CENTER_WIDTH / 2, y, rightWidth - CENTER_WIDTH / 2, height); leftTreeViewer.getControl().redraw(); rightTreeViewer.getControl().redraw(); getCenterPart().redraw(); } public void refreshCenter() { canvas.redraw(); canvas.update(); } private Canvas getCenterPart() { return canvas; } @Override public Composite createCenterControl(Composite parent) { canvas = new BufferedCanvas((Composite) getControl(), SWT.TRANSPARENT) { public void doPaint(GC gc) { try { drawDiffLines(gc); } catch (Exception e) { ComparePlugin.writeToLog("Exception during difference line drawing", e, getClass()); } } }; return canvas; } protected void drawDiffLines(GC gc) { // if the differencer is null then we are currently // in the middle of a content update, skip this paint // request if (differencer == null) { return; } gc.setAdvanced(true); gc.setAntialias(SWT.ON); List<TreeDifference> differences = differencer.getLeftDifferences(); for (TreeDifference difference : differences) { if (SynchronizedTreeViewer.differenceIsGraphical(difference) && !debug) { // currently do not include graphical data continue; } gc.setForeground(getColor(PlatformUI.getWorkbench().getDisplay(), getStrokeColor(difference))); TreeItem leftItem = leftTreeViewer.getItemForDifference(difference); if (leftItem == null || leftItem.isDisposed()) { continue; } TreeItem matchingItem = SynchronizedTreeViewer.getMatchingItem(leftItem.getData(), rightTreeViewer); if (matchingItem == null) { continue; } Rectangle leftBounds = leftTreeViewer.buildHighlightRectangle(leftItem, false, gc, true, true); Rectangle rightBounds = rightTreeViewer.buildHighlightRectangle(matchingItem, false, gc, true, true); // draw a line from the left edge to the right edge int[] points = getCenterCurvePoints(0, leftBounds.y + (leftBounds.height / 2), canvas.getBounds().width, rightBounds.y + (rightBounds.height / 2)); for (int i = 1; i < points.length; i++) { gc.drawLine(i - 1, points[i - 1], i, points[i]); } } } private int[] getCenterCurvePoints(int startx, int starty, int endx, int endy) { if (basicCenterCurve == null) buildBaseCenterCurve(endx - startx); double height = endy - starty; endy = endy + 2; starty = starty + 2; height = height / 2; int width = endx - startx; int[] points = new int[width]; for (int i = 0; i < width; i++) { points[i] = (int) (-height * basicCenterCurve[i] + height + starty); } return points; } private void buildBaseCenterCurve(int w) { double width = w; basicCenterCurve = new double[CENTER_WIDTH]; for (int i = 0; i < CENTER_WIDTH; i++) { double r = i / width; basicCenterCurve[i] = Math.cos(Math.PI * r); } } @Override public void setInput(Object input) { oldInput = getInput(); super.setInput(input); } @Override protected void updateContent(Object ancestor, Object left, Object right) { // make left and right non-edittable as either we were saved // or there were changes outside of the compare editor setLeftDirty(false); setRightDirty(false); boolean leftEditable = getCompareConfiguration().isLeftEditable(); boolean rightEditable = getCompareConfiguration().isRightEditable(); if (left instanceof IEditableContent) { leftEditable = ((IEditableContent) left).isEditable(); } else { leftEditable = false; } if (right instanceof IEditableContent) { rightEditable = ((IEditableContent) right).isEditable(); } else { rightEditable = false; } getLeftViewer().setEditable(leftEditable); getRightViewer().setEditable(rightEditable); reconnectLocalDocuments(ancestor, left, right); if (left == null && right == null) { return; } if (((left instanceof IStreamContentAccessor || right instanceof IStreamContentAccessor) || ((left instanceof IStreamContentAccessor) && right == null))) { try { instanceMap.remove(oldInput); Object leftKey = ModelCacheManager.getLeftKey(getInput()); Object rightKey = ModelCacheManager.getRightKey(getInput()); Object ancestorKey = ModelCacheManager.getAncestorKey(getInput()); ModelCacheManager modelCacheManager = ComparePlugin.getDefault().getModelCacheManager(); NonRootModelElement[] leftElements = null; NonRootModelElement[] rightElements = null; NonRootModelElement[] ancestorElements = null; if (left instanceof IStreamContentAccessor) { leftElements = modelCacheManager.getRootElements(left, this, false, getLeftCompareRoot(), leftKey); } if (right instanceof IStreamContentAccessor) { rightElements = modelCacheManager.getRootElements(right, this, false, getRightCompareRoot(), rightKey); } if (ancestor instanceof IStreamContentAccessor) { ancestorElements = modelCacheManager.getRootElements(ancestor, this, false, getAncestorCompareRoot(), ancestorKey); } if (leftElements.length != 0) { ICompareInput visited = visitedModels.get(leftElements[0]); if (visited == null) { visitedModels.put(leftElements[0], (ICompareInput) getInput()); } } else if (rightElements.length != 0) { ICompareInput visited = visitedModels.get(rightElements[0]); if (visited == null) { visitedModels.put(rightElements[0], (ICompareInput) getInput()); } } // configure cache key for left, right and ancestor label providers ((ModelCompareContentProvider) leftTreeViewer.getContentProvider()).setCacheKey(leftKey); ((ModelCompareContentProvider) rightTreeViewer.getContentProvider()).setCacheKey(rightKey); if (ancestorTreeViewer != null) { ((ModelCompareContentProvider) ancestorTreeViewer.getContentProvider()) .setCacheKey(ancestorKey); } ((ModelCompareContentProvider) leftTreeViewer.getContentProvider()) .setModelRoot(getLeftCompareRoot()); ((ModelCompareContentProvider) rightTreeViewer.getContentProvider()) .setModelRoot(getRightCompareRoot()); if (ancestorTreeViewer != null) { ((ModelCompareContentProvider) ancestorTreeViewer.getContentProvider()) .setModelRoot(getAncestorCompareRoot()); } ModelCompareContentProvider differencerProvider = new ModelCompareContentProvider(); differencerProvider.setRootElements(leftElements, rightElements); if (differencer != null) { differencer.dipose(); } differencer = new TreeDifferencer(differencerProvider, leftElements, rightElements, ancestorElements, isThreeWay(), getInput()); TreeDifferencer.instances.put(getInput(), differencer); updateRootElementsForTreeViewers(leftElements, rightElements); nextDifference.getAction().setEnabled(false); nextDifference.getAction().setEnabled(nextDifference.getAction().isEnabled()); previousDifference.getAction().setEnabled(false); previousDifference.getAction().setEnabled(previousDifference.getAction().isEnabled()); instanceMap.put(getInput(), this); // set input on any extending viewers for (ModelMergeViewer viewer : leftExtensions) { viewer.setCompareRoot(getLeftCompareRoot()); viewer.setKey(leftKey); viewer.setInput(getInput()); } for (ModelMergeViewer viewer : rightExtensions) { viewer.setCompareRoot(getRightCompareRoot()); viewer.setKey(rightKey); viewer.setInput(getInput()); } if (left instanceof IStreamContentAccessor && leftIsLocal()) { // here we will automatically merge any incoming/conflicting // graphics this will cover the case where only graphics // are changed List<TreeDifference> graphicalDifferences = getIncomingGraphicalDifferences(leftIsLocal()); if (!graphicalDifferences.isEmpty()) { mergeIncomingGraphicalChanges(graphicalDifferences, leftIsLocal(), (ICompareInput) getInput()); markLeftDirty(true); differencer.refresh(); } } if (right instanceof IStreamContentAccessor && !leftIsLocal()) { // here we will automatically merge any incoming/conflicting // graphics this will cover the case where only graphics // are changed List<TreeDifference> graphicalDifferences = getIncomingGraphicalDifferences(!leftIsLocal()); if (!graphicalDifferences.isEmpty()) { mergeIncomingGraphicalChanges(graphicalDifferences, !leftIsLocal(), (ICompareInput) getInput()); markRightDirty(true); differencer.refresh(); } } } catch (ModelLoadException e) { CorePlugin.logError("Unable to load data for comparison.", e); } } if (ancestor != null) { ancestorTreeViewer.setInput(ancestor); } leftTreeViewer.setInput(left); rightTreeViewer.setInput(right); refreshCenter(); PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { if (differencer != null) { revealAndSelectDifference(differencer.getNextDifference(null)); } } }); } private void updateRootElementsForTreeViewers(NonRootModelElement[] leftElements, NonRootModelElement[] rightElements) { // update the root elements for each tree viewer, this is // to support empty elements ((ModelCompareContentProvider) leftTreeViewer.getContentProvider()).setRootElements(leftElements, rightElements); ((ModelCompareContentProvider) rightTreeViewer.getContentProvider()).setRootElements(leftElements, rightElements); if (ancestorTreeViewer != null) { ((ModelCompareContentProvider) ancestorTreeViewer.getContentProvider()).setRootElements(leftElements, rightElements); } } private List<TreeDifference> getIncomingGraphicalDifferences(boolean left) { List<TreeDifference> incomingGraphicalDifferences = new ArrayList<TreeDifference>(); List<TreeDifference> differences = differencer.getRightDifferences(); if (!left) { differences = differencer.getLeftDifferences(); } for (TreeDifference difference : differences) { // only copy those that are incoming or conflicting // also exclude any additions or removals where the // semantical model does not match int diffKind = difference.getKind() & Differencer.DIRECTION_MASK; if (((left && diffKind == Differencer.RIGHT) || (!left && diffKind == Differencer.LEFT) || diffKind == Differencer.CONFLICTING && differencer.isThreeWay()) || ((difference.getKind() == Differencer.ADDITION) || (difference.getKind() == Differencer.DELETION))) { if (SynchronizedTreeViewer.differenceIsGraphical(difference)) { boolean add = true; if ((difference.getElement() == null || difference.getElement() instanceof EmptyElement) && difference.getMatchingDifference() .getElement() instanceof NonRootModelElementComparable) { // this is a removal, only remove if the // semantic element represented is also // removed Object elementToBeRemoved = ((NonRootModelElementComparable) difference .getMatchingDifference().getElement()).getRealElement(); if (elementToBeRemoved instanceof GraphicalElement_c) { GraphicalElement_c graphEleToRemove = (GraphicalElement_c) elementToBeRemoved; // if the graphical element represents value is null // we need to // look at the local file, in some cases the // configuration // management system may have pre-merged the // semantical element // in a different file if (graphEleToRemove.getRepresents() == null) { ElementSpecification_c spec = ElementSpecification_c .getOneGD_ESOnR10(graphEleToRemove); if (spec != null) { NonRootModelElement elementGlobally = (NonRootModelElement) Ooaofooa .getDefaultInstance().getInstanceList(spec.getRepresents()) .getGlobal(graphEleToRemove.getOoa_id()); if (elementGlobally != null) { add = false; } } } if (add) { if (graphEleToRemove.getRepresents() != null && graphEleToRemove.getRepresents() instanceof NonRootModelElement) { NonRootModelElement semanticElement = (NonRootModelElement) graphEleToRemove .getRepresents(); if (!semanticElement.isOrphaned()) { add = false; } else { addConnectionPoints(graphEleToRemove, left, incomingGraphicalDifferences); } } } } else { add = false; } } if ((difference.getMatchingDifference().getElement() == null || difference.getMatchingDifference().getElement() instanceof EmptyElement) && difference.getElement() instanceof NonRootModelElementComparable) { // this is an addition, only add the graphic if the // semantic element was also copied Object elementToBeAdded = ((NonRootModelElementComparable) difference.getElement()) .getRealElement(); if (elementToBeAdded instanceof GraphicalElement_c) { GraphicalElement_c graphEleToAdd = (GraphicalElement_c) elementToBeAdded; // if the graphical element represents value is null we need to // look at the local file, in some cases the configuration // management system may have pre-merged the semantical element // in a different file if (graphEleToAdd.getRepresents() == null) { ElementSpecification_c spec = ElementSpecification_c .getOneGD_ESOnR10(graphEleToAdd); if (spec != null) { NonRootModelElement elementGlobally = (NonRootModelElement) Ooaofooa .getDefaultInstance().getInstanceList(spec.getRepresents()) .getGlobal(graphEleToAdd.getOoa_id()); if (elementGlobally == null) { add = false; } else { addConnectionPoints(graphEleToAdd, left, incomingGraphicalDifferences); } } } if (graphEleToAdd.getRepresents() != null && graphEleToAdd.getRepresents() instanceof NonRootModelElement) { NonRootModelElement semanticElement = (NonRootModelElement) graphEleToAdd .getRepresents(); NonRootModelElement parentElement = (NonRootModelElement) ((EmptyElement) difference .getMatchingDifference().getElement()).getParent(); Object existingSemanticElement = Ooaofooa .getInstance(parentElement.getModelRoot().getId()) .getInstanceList(semanticElement.getClass()) .get(semanticElement.getInstanceKey()); if (existingSemanticElement == null) { add = false; } else { addConnectionPoints(graphEleToAdd, left, incomingGraphicalDifferences); } } } else { add = false; } } if (add) { incomingGraphicalDifferences.add(difference); } } } } return incomingGraphicalDifferences; } private void addConnectionPoints(GraphicalElement_c graphEleToAdd, boolean left, List<TreeDifference> incomingGraphicalDifferences) { // we need to include any DIM_CON additions (connections to existing shapes) Connector_c connector = Connector_c.getOneGD_CONOnR2(graphEleToAdd); if (connector != null) { Graphconnector_c startCon = Graphconnector_c .getOneDIM_CONOnR320(Graphedge_c.getOneDIM_EDOnR20(connector)); Graphconnector_c endCon = Graphconnector_c .getOneDIM_CONOnR321(Graphedge_c.getManyDIM_EDsOnR20(connector)); if (startCon != null) { ComparableTreeObject comparableTreeObject = ComparableProvider.getComparableTreeObject(startCon); List<TreeDifference> startConDiffs = differencer.getDifferences(comparableTreeObject, !left); incomingGraphicalDifferences.addAll(startConDiffs); } if (endCon != null) { ComparableTreeObject comparableTreeObject = ComparableProvider.getComparableTreeObject(endCon); List<TreeDifference> endConDiffs = differencer.getDifferences(comparableTreeObject, !left); incomingGraphicalDifferences.addAll(endConDiffs); } } } /** * Reconnects the document providers to reset the modification state. Left * and Right modifications are not always done on a document, but the tree * states are kept in sync. Therefore we reset the connection to allow * resetting of the current modification state. * * @param ancestor * @param left * @param right */ private void reconnectLocalDocuments(Object ancestor, Object left, Object right) { if (left instanceof IAdaptable) { SharedDocumentAdapter sharedDocumentAdapter = (SharedDocumentAdapter) ((IAdaptable) left) .getAdapter(ISharedDocumentAdapter.class); if (sharedDocumentAdapter != null) { IEditorInput documentKey = sharedDocumentAdapter.getDocumentKey(left); if (documentKey != null) { IDocumentProvider documentProvider = SharedDocumentAdapter.getDocumentProvider(documentKey); sharedDocumentAdapter.disconnect(left); try { sharedDocumentAdapter.connect(documentProvider, sharedDocumentAdapter.getDocumentKey(left)); } catch (CoreException e) { ComparePlugin.writeToLog("Unable to reconnect to left changed document.", e, getClass()); } } } } if (right instanceof IAdaptable) { SharedDocumentAdapter sharedDocumentAdapter = (SharedDocumentAdapter) ((IAdaptable) right) .getAdapter(ISharedDocumentAdapter.class); if (sharedDocumentAdapter != null) { IEditorInput key = sharedDocumentAdapter.getDocumentKey(right); if (key != null) { IDocumentProvider documentProvider = SharedDocumentAdapter.getDocumentProvider(key); sharedDocumentAdapter.disconnect(right); try { sharedDocumentAdapter.connect(documentProvider, sharedDocumentAdapter.getDocumentKey(right)); } catch (CoreException e) { ComparePlugin.writeToLog("Unable to reconnect to right changed document.", e, getClass()); } } } } } public TreeDifferencer getDifferencer() { return differencer; } public SynchronizedTreeViewer getLeftViewer() { return leftTreeViewer; } public SynchronizedTreeViewer getAncestorTree() { return ancestorTreeViewer; } @Override protected void handleDispose(DisposeEvent event) { // persist any non-saved left inputs Set<NonRootModelElement> elements = visitedModels.keySet(); for (NonRootModelElement element : elements) { ICompareInput saved = savedModels.get(element); if (saved == null) { // only do this for any that have the // git annotations NonRootModelElement elementGlobally = (NonRootModelElement) Ooaofooa.getDefaultInstance() .getInstanceList(element.getClass()).getGlobal(element.getInstanceKey()); if (elementGlobally != null) { // refresh the file to make sure its // synced with what git wrote InputStream actualContents = null; try { elementGlobally.getFile().refreshLocal(IFile.DEPTH_ONE, new NullProgressMonitor()); actualContents = elementGlobally.getFile().getContents(); } catch (CoreException e) { CorePlugin.logError("Unable to load local file for conflict annotation removal.", e); } if (actualContents != null) { String data = readData(actualContents); if (data.contains(">>>>>>>") && data.contains("<<<<<<<") && data.contains("=======")) { // we need to reload the data from the input, incase // a user has modified and the closed without saving ICompareInput input = (ICompareInput) visitedModels.get(element); Ooaofooa compareRoot = Ooaofooa .getInstance(ModelRoot.getLeftCompareRootPrefix() + input.hashCode()); try { ITypedElement left = input.getLeft(); if (left instanceof IStreamContentAccessor) { IStreamContentAccessor accesor = (IStreamContentAccessor) left; InputStream contents = accesor.getContents(); String compareData = readData(contents); ByteArrayInputStream bais = new ByteArrayInputStream(compareData.getBytes()); modelManager.getRootElements(left, this, true, compareRoot, ModelCacheManager.getLeftKey(input)); elementGlobally.getFile().setContents(bais, IResource.FORCE, new NullProgressMonitor()); } } catch (CoreException e) { CorePlugin.logError( "Unable to update local contents to remove conflict annotations.", e); } catch (ModelLoadException e) { CorePlugin.logError( "Unable to load compare contents to remove conflict annotations.", e); } } } } } } for (NonRootModelElement element : elements) { ICompareInput previous = visitedModels.get(element); ITypedElement oldLeft = previous.getLeft(); ITypedElement oldRight = previous.getRight(); ITypedElement oldAncestor = previous.getAncestor(); ModelCacheManager modelCacheManager = ComparePlugin.getDefault().getModelCacheManager(); modelCacheManager.releaseModel(oldLeft, this, ModelCacheManager.getLeftKey(previous)); modelCacheManager.releaseModel(oldRight, this, ModelCacheManager.getRightKey(previous)); modelCacheManager.releaseModel(oldAncestor, this, ModelCacheManager.getAncestorKey(previous)); } visitedModels.clear(); savedModels.clear(); differencer.dipose(); instanceMap.remove(getInput()); super.handleDispose(event); if (canvas != null) { canvas.dispose(); canvas = null; } differencer = null; leftTreeViewer = null; rightTreeViewer = null; ancestorTreeViewer = null; if (compareTransactionManager != null) { // unregister transaction listener TransactionManager.getSingleton().removeTransactionListener(compareTransactionManager); } compareTransactionManager = null; if (colors != null) { Set<RGB> keySet = colors.keySet(); for (RGB rgb : keySet) { Color color = colors.get(rgb); if (color != null) { color.dispose(); } } colors.clear(); } ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); } private String readData(InputStream contents) { try { byte[] resultBuff = new byte[0]; byte[] buff = new byte[1024]; int i = -1; while ((i = contents.read(buff, 0, buff.length)) > -1) { byte[] tbuff = new byte[resultBuff.length + i]; System.arraycopy(resultBuff, 0, tbuff, 0, resultBuff.length); System.arraycopy(buff, 0, tbuff, resultBuff.length, i); resultBuff = tbuff; } String result = new String(resultBuff); contents.close(); return result; } catch (IOException e) { CorePlugin.logError("Unable to read compare contents.", e); } return ""; } public CompareTransactionManager getCompareTransactionManager() { if (compareTransactionManager == null) { compareTransactionManager = new CompareTransactionManager(); } return compareTransactionManager; } public void markLeftDirty(boolean value) { setLeftDirty(value); } public void markRightDirty(boolean value) { setRightDirty(value); } public void updateColors() { ColorRegistry registry = JFaceResources.getColorRegistry(); RGB bg = getBackground(PlatformUI.getWorkbench().getDisplay()); INCOMING_BASE = registry.getRGB(INCOMING_COLOR); if (INCOMING_BASE == null) { INCOMING_BASE = new RGB(0, 0, 255); registry.put(INCOMING_COLOR, INCOMING_BASE); } INCOMING = interpolate(INCOMING_BASE, bg, 0.6); OUTGOING_BASE = registry.getRGB(OUTGOING_COLOR); if (OUTGOING_BASE == null) { OUTGOING_BASE = new RGB(0, 0, 0); // BLACK registry.put(OUTGOING_COLOR, OUTGOING_BASE); } OUTGOING = interpolate(OUTGOING_BASE, bg, 0.6); CONFLICT_BASE = registry.getRGB(CONFLICTING_COLOR); if (CONFLICT_BASE == null) { CONFLICT_BASE = new RGB(255, 0, 0); registry.put(CONFLICTING_COLOR, CONFLICT_BASE); // RED } CONFLICT = interpolate(CONFLICT_BASE, bg, 0.6); RESOLVED = registry.getRGB(RESOLVED_COLOR); if (RESOLVED == null) { RESOLVED = new RGB(0, 255, 0); // GREEN } CONTAINED = registry.getRGB(CONTAINED_COLOR); if (CONTAINED == null) { CONTAINED = new RGB(50, 255, 25); } } private RGB getBackground(Display display) { return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND).getRGB(); } static RGB interpolate(RGB fg, RGB bg, double scale) { if (fg != null && bg != null) return new RGB((int) ((1.0 - scale) * fg.red + scale * bg.red), (int) ((1.0 - scale) * fg.green + scale * bg.green), (int) ((1.0 - scale) * fg.blue + scale * bg.blue)); if (fg != null) return fg; if (bg != null) return bg; return new RGB(128, 128, 128); // a gray } RGB getStrokeColor(TreeDifference difference) { updateColors(); boolean ignoreAncestor = Utilities.getBoolean(getCompareConfiguration(), ICompareUIConstants.PROP_IGNORE_ANCESTOR, false); if (difference.isContainedDifference()) { return CONTAINED; } if (isThreeWay() && !ignoreAncestor) { switch (difference.getKind() & Differencer.DIRECTION_MASK) { case Differencer.RIGHT: if (leftIsLocal()) return INCOMING; return OUTGOING; case Differencer.LEFT: if (leftIsLocal()) return OUTGOING; return INCOMING; case Differencer.CONFLICTING: return CONFLICT; } return OUTGOING; } return OUTGOING; } Color getColor(Display display, RGB rgb) { if (rgb == null) return null; if (colors == null) colors = new HashMap<RGB, Color>(20); Color c = (Color) colors.get(rgb); if (c == null) { c = new Color(display, rgb); colors.put(rgb, c); } return c; } private boolean leftIsLocal() { return Utilities.getBoolean(getCompareConfiguration(), "LEFT_IS_LOCAL", false); //$NON-NLS-1$ } public SynchronizedTreeViewer getRightViewer() { return rightTreeViewer; } public IAction getUndoAction() { return undo; } public IAction getRedoAction() { return redo; } public Ooaofooa getLeftCompareRoot() { if (getInput() != null) { return Ooaofooa.getInstance(ModelRoot.getLeftCompareRootPrefix() + getInput().hashCode()); } if (oldInput != null) { return Ooaofooa.getInstance(ModelRoot.getLeftCompareRootPrefix() + oldInput.hashCode()); } return null; } public Ooaofooa getRightCompareRoot() { if (getInput() != null) { return Ooaofooa.getInstance(ModelRoot.getRightCompareRootPrefix() + getInput().hashCode()); } if (oldInput != null) { return Ooaofooa.getInstance(ModelRoot.getRightCompareRootPrefix() + oldInput.hashCode()); } return null; } public Ooaofooa getAncestorCompareRoot() { if (getInput() != null) { return Ooaofooa.getInstance(ModelRoot.getAncestorCompareRootPrefix() + getInput().hashCode()); } if (oldInput != null) { return Ooaofooa.getInstance(ModelRoot.getAncestorCompareRootPrefix() + oldInput.hashCode()); } return null; } public static ModelContentMergeViewer getInstance(Object input) { if (input == null) { Set<Object> keySet = instanceMap.keySet(); if (keySet.iterator().hasNext()) { return instanceMap.get(keySet.iterator().next()); } else { return null; } } return instanceMap.get(input); } public void revealAndSelectItem(Object element) { getLeftViewer().setSelection(new StructuredSelection(element), true); TreeItem leftItem = SynchronizedTreeViewer.getMatchingItem(element, getLeftViewer()); if (leftItem != null) { getLeftViewer().getTree().setTopItem(leftItem); } else { } getRightViewer().setSelection(new StructuredSelection(element), true); TreeItem rightItem = SynchronizedTreeViewer.getMatchingItem(element, getRightViewer()); if (rightItem != null) { getRightViewer().getTree().setTopItem(rightItem); } else { } if (getAncestorTree() != null) { TreeItem ancestorItem = SynchronizedTreeViewer.getMatchingItem(element, getAncestorTree()); if (ancestorItem != null) { getAncestorTree().getTree().setTopItem(ancestorItem); } getAncestorTree().setSelection(new StructuredSelection(element), true); } refreshCenter(); } @Override public void setCopySelection(boolean value) { copySelection = value; } @Override public CompareConfiguration internalGetCompareConfiguration() { return getCompareConfiguration(); } @Override public boolean isLeftEditable() { IMergeViewerContentProvider cp = (IMergeViewerContentProvider) getContentProvider(); if (cp != null) { if (!isPatchHunk()) { boolean result = cp.isLeftEditable(getInput()); if (result) { // also check if the file is writable, for some reason // eclipse does not if (getInput() instanceof ICompareInput) { Object left = ((ICompareInput) getInput()).getLeft(); if (left instanceof LocalResourceTypedElement) { ResourceAttributes resourceAttributes = ((LocalResourceTypedElement) left).getResource() .getResourceAttributes(); return !resourceAttributes.isReadOnly(); } } return result; } } } return false; } private boolean isPatchHunk() { return Utilities.isHunk(getInput()); } @Override public boolean isRightEditable() { IMergeViewerContentProvider cp = (IMergeViewerContentProvider) getContentProvider(); if (cp != null) { if (!isPatchHunk()) { boolean result = cp.isRightEditable(getInput()); if (result) { // also check if the file is writable, for some reason // eclipse does not if (getInput() instanceof ICompareInput) { Object right = ((ICompareInput) getInput()).getRight(); if (right instanceof LocalResourceTypedElement) { ResourceAttributes resourceAttributes = ((LocalResourceTypedElement) right) .getResource().getResourceAttributes(); return !resourceAttributes.isReadOnly(); } } return result; } } } return false; } @Override public void resourceChanged(IResourceChangeEvent event) { boolean refresh = false; IResourceDelta originalDelta = event.getDelta(); IResourceDelta delta = null; if (originalDelta != null) { IResource[] resources = getResourcesFromInput(); for (IResource resource : resources) { delta = originalDelta.findMember(resource.getFullPath()); if (delta != null) { break; } } } if (delta != null) { final int flags = delta.getFlags(); switch (delta.getKind()) { case IResourceDelta.CHANGED: if ((IResourceDelta.CONTENT & flags) != 0) { refresh = true; } break; } } if (refresh) { PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { @Override public void run() { if (getInput() != null) { updateContent(((ICompareInput) getInput()).getAncestor(), ((ICompareInput) getInput()).getLeft(), ((ICompareInput) getInput()).getRight()); ModelStructureDiffViewer modelStructureDiffViewer = ModelStructureDiffViewer.inputMap .get(getInput()); if (modelStructureDiffViewer != null) { modelStructureDiffViewer.refreshModel(); } } } }); } } private IResource[] getResourcesFromInput() { List<IResource> resources = new ArrayList<IResource>(); Object compareInput = getInput(); if (compareInput instanceof ICompareInput) { ICompareInput cinput = (ICompareInput) compareInput; ITypedElement left = cinput.getLeft(); ITypedElement right = cinput.getRight(); ITypedElement anc = cinput.getAncestor(); if (left instanceof LocalResourceTypedElement) { IResource leftResource = ((LocalResourceTypedElement) left).getResource(); resources.add(leftResource); } if (right instanceof LocalResourceTypedElement) { IResource rightResource = ((LocalResourceTypedElement) right).getResource(); resources.add(rightResource); } if (anc != null && anc instanceof LocalResourceTypedElement) { IResource ancResource = ((LocalResourceTypedElement) anc).getResource(); resources.add(ancResource); } } return resources.toArray(new IResource[resources.size()]); } public static void removeContainerDifferences(List<TreeDifference> differences, List<TreeDifference> mergeDifferences) { for (TreeDifference difference : differences) { if (difference.isContainedDifference()) { TreeDifference container = difference.getContainerDifference(); if (container != null && !mergeDifferences.contains(container)) { mergeDifferences.add(container); } // if for some reason there is no container leave the // difference out of the merge } else { mergeDifferences.add(difference); } } } }