| /* |
| * Copyright (c) 2012, 2013, 2015 Eike Stepper (Berlin, Germany) 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: |
| * Eike Stepper - initial API and implementation |
| */ |
| package org.eclipse.emf.cdo.ui.compare; |
| |
| import org.eclipse.emf.cdo.CDOObject; |
| import org.eclipse.emf.cdo.CDOState; |
| import org.eclipse.emf.cdo.common.branch.CDOBranchPoint; |
| import org.eclipse.emf.cdo.common.commit.CDOCommitInfo; |
| import org.eclipse.emf.cdo.common.id.CDOID; |
| import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; |
| import org.eclipse.emf.cdo.compare.CDOCompare; |
| import org.eclipse.emf.cdo.compare.CDOCompareUtil; |
| import org.eclipse.emf.cdo.session.CDORepositoryInfo; |
| import org.eclipse.emf.cdo.session.CDOSession; |
| import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta; |
| import org.eclipse.emf.cdo.transaction.CDOTransaction; |
| import org.eclipse.emf.cdo.transaction.CDOTransactionOpener; |
| import org.eclipse.emf.cdo.ui.CDOItemProvider; |
| import org.eclipse.emf.cdo.ui.internal.compare.bundle.OM; |
| import org.eclipse.emf.cdo.util.CDOUtil; |
| import org.eclipse.emf.cdo.view.CDOView; |
| import org.eclipse.emf.cdo.view.CDOViewOpener; |
| |
| import org.eclipse.net4j.util.lifecycle.LifecycleUtil; |
| import org.eclipse.net4j.util.ui.UIUtil; |
| |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.util.BasicEList; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.compare.Comparison; |
| import org.eclipse.emf.compare.Diff; |
| import org.eclipse.emf.compare.DifferenceState; |
| import org.eclipse.emf.compare.Match; |
| import org.eclipse.emf.compare.domain.ICompareEditingDomain; |
| import org.eclipse.emf.compare.domain.impl.EMFCompareEditingDomain; |
| import org.eclipse.emf.compare.scope.IComparisonScope; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; |
| import org.eclipse.emf.edit.EMFEditPlugin; |
| import org.eclipse.emf.edit.provider.ComposedAdapterFactory; |
| import org.eclipse.emf.spi.cdo.InternalCDOObject; |
| import org.eclipse.emf.spi.cdo.InternalCDOTransaction; |
| import org.eclipse.emf.spi.cdo.InternalCDOView; |
| |
| import org.eclipse.compare.CompareConfiguration; |
| import org.eclipse.compare.CompareUI; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.util.IPropertyChangeListener; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.widgets.Shell; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Static methods to open an EMF Compare dialog. |
| * |
| * @author Eike Stepper |
| * @since 4.2 |
| */ |
| public class CDOCompareEditorUtil |
| { |
| private static final ThreadLocal<Boolean> ACTIVATE_EDITOR = new ThreadLocal<Boolean>(); |
| |
| private static final ThreadLocal<Boolean> SUPPRESS_COMMIT = new ThreadLocal<Boolean>(); |
| |
| private static final ThreadLocal<List<Runnable>> DISPOSE_RUNNABLES = new ThreadLocal<List<Runnable>>(); |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean openEditor(CDOViewOpener viewOpener, CDOBranchPoint leftPoint, CDOBranchPoint rightPoint, |
| CDOView[] originView, boolean activate) |
| { |
| return openEditor(viewOpener, null, leftPoint, rightPoint, originView, activate); |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean openEditor(CDOViewOpener viewOpener, CDOTransactionOpener transactionOpener, |
| CDOBranchPoint leftPoint, CDOBranchPoint rightPoint, CDOView[] originView, boolean activate) |
| { |
| ACTIVATE_EDITOR.set(activate); |
| |
| try |
| { |
| return openDialog(viewOpener, transactionOpener, leftPoint, rightPoint, originView); |
| } |
| finally |
| { |
| ACTIVATE_EDITOR.remove(); |
| } |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean openEditor(CDOCommitInfo rightCommitInfo, CDOBranchPoint leftPoint, boolean activate) |
| { |
| ACTIVATE_EDITOR.set(activate); |
| |
| try |
| { |
| return openDialog(rightCommitInfo, leftPoint); |
| } |
| finally |
| { |
| ACTIVATE_EDITOR.remove(); |
| } |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean openEditor(CDOCommitInfo commitInfo, boolean activate) |
| { |
| ACTIVATE_EDITOR.set(activate); |
| |
| try |
| { |
| return openDialog(commitInfo); |
| } |
| finally |
| { |
| ACTIVATE_EDITOR.remove(); |
| } |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean openEditor(CDOView leftView, CDOView rightView, CDOView[] originView, boolean activate) |
| { |
| ACTIVATE_EDITOR.set(activate); |
| |
| try |
| { |
| return openDialog(leftView, rightView, originView); |
| } |
| finally |
| { |
| ACTIVATE_EDITOR.remove(); |
| } |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean openDialog(CDOSession session, CDOBranchPoint leftPoint, CDOBranchPoint rightPoint) |
| { |
| return openDialog(session, leftPoint, rightPoint, null); |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean openDialog(CDOViewOpener viewOpener, CDOBranchPoint leftPoint, CDOBranchPoint rightPoint, |
| CDOView[] originView) |
| { |
| return openDialog(viewOpener, null, leftPoint, rightPoint, originView); |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean openDialog(CDOViewOpener viewOpener, CDOTransactionOpener transactionOpener, |
| CDOBranchPoint leftPoint, CDOBranchPoint rightPoint, CDOView[] originView) |
| { |
| final Boolean activateEditor = ACTIVATE_EDITOR.get(); |
| final CDOView[] leftAndRightView = { null, null }; |
| |
| addDisposeRunnables(new Runnable() |
| { |
| public void run() |
| { |
| LifecycleUtil.deactivate(leftAndRightView[0]); |
| LifecycleUtil.deactivate(leftAndRightView[1]); |
| } |
| }); |
| |
| try |
| { |
| leftAndRightView[0] = viewOpener.openView(leftPoint, new ResourceSetImpl()); |
| |
| ResourceSet rightResourceSet = new ResourceSetImpl(); |
| if (transactionOpener != null) |
| { |
| rightPoint = rightPoint.getBranch().getHead(); |
| leftAndRightView[1] = transactionOpener.openTransaction(rightPoint, rightResourceSet); |
| } |
| else |
| { |
| leftAndRightView[1] = viewOpener.openView(rightPoint, rightResourceSet); |
| } |
| |
| return openDialog(leftAndRightView[0], leftAndRightView[1], originView); |
| } |
| finally |
| { |
| if (activateEditor == null) |
| { |
| List<Runnable> list = removeDisposeRunnables(); |
| runDisposeRunnables(list); |
| } |
| } |
| } |
| |
| public static boolean openDialog(CDOCommitInfo rightCommitInfo, CDOBranchPoint leftPoint) |
| { |
| CDORepositoryInfo repositoryInfo = (CDORepositoryInfo)rightCommitInfo.getCommitInfoManager().getRepository(); |
| CDOSession session = repositoryInfo.getSession(); |
| |
| return openDialog(session, leftPoint, rightCommitInfo); |
| } |
| |
| public static boolean openDialog(CDOCommitInfo commitInfo) |
| { |
| long previousTimeStamp = commitInfo.getPreviousTimeStamp(); |
| if (previousTimeStamp == CDOBranchPoint.UNSPECIFIED_DATE) |
| { |
| return false; |
| } |
| |
| CDOBranchPoint previous = CDOBranchUtil.normalizeBranchPoint(commitInfo.getBranch(), previousTimeStamp); |
| return openDialog(commitInfo, previous); |
| } |
| |
| public static boolean openDialog(CDOView leftView, CDOView rightView, CDOView[] originView) |
| { |
| return openDialog(leftView, rightView, originView, CDOCompareUtil.DEFAULT_VIEW_OPENER); |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean openDialog(CDOView leftView, final CDOView rightView, CDOView[] originView, |
| CDOViewOpener viewOpener) |
| { |
| final Input input = createComparisonInput(leftView, rightView, originView, viewOpener); |
| |
| if (input == null) |
| { |
| UIUtil.getDisplay().syncExec(new Runnable() |
| { |
| public void run() |
| { |
| Shell shell = UIUtil.getShell(); |
| MessageDialog.openInformation(shell, "Compare", "There are no differences between the selected inputs."); |
| } |
| }); |
| |
| return false; |
| } |
| |
| final Boolean activateEditor = ACTIVATE_EDITOR.get(); |
| if (activateEditor != null) |
| { |
| List<Runnable> disposeRunnables = removeDisposeRunnables(); |
| input.setDisposeRunnables(disposeRunnables); |
| |
| UIUtil.getDisplay().asyncExec(new Runnable() |
| { |
| public void run() |
| { |
| CompareUI.openCompareEditor(input, activateEditor); |
| } |
| }); |
| } |
| else |
| { |
| final EList<Diff> differences = new BasicEList<Diff>(); |
| |
| UIUtil.getDisplay().syncExec(new Runnable() |
| { |
| public void run() |
| { |
| CompareUI.openCompareDialog(input); |
| |
| if (rightView instanceof InternalCDOTransaction) |
| { |
| Comparison comparison = input.getComparison(); |
| differences.addAll(comparison.getDifferences()); |
| } |
| } |
| }); |
| |
| if (!differences.isEmpty() && rightView instanceof InternalCDOTransaction) |
| { |
| if (!handleMerges((InternalCDOTransaction)rightView, differences)) |
| { |
| return false; |
| } |
| } |
| } |
| |
| return input.isOK(); |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static Input createComparisonInput(CDOView leftView, CDOView rightView, CDOView[] originView, |
| CDOViewOpener viewOpener) |
| { |
| Comparison comparison = CDOCompareUtil.compare(leftView, rightView, originView, viewOpener); |
| if (comparison.getDifferences().isEmpty()) |
| { |
| return null; |
| } |
| |
| IComparisonScope scope = CDOCompare.getScope(comparison); |
| ICompareEditingDomain editingDomain = EMFCompareEditingDomain.create(scope.getLeft(), scope.getRight(), |
| scope.getOrigin()); |
| |
| ComposedAdapterFactory.Descriptor.Registry registry = EMFEditPlugin.getComposedAdapterFactoryDescriptorRegistry(); |
| ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(registry); |
| |
| CDOBranchPoint leftBranchPoint = CDOBranchUtil.copyBranchPoint(leftView); |
| CDOBranchPoint rightBranchPoint = CDOBranchUtil.copyBranchPoint(rightView); |
| |
| CDOItemProvider itemProvider = new CDOItemProvider(null) |
| { |
| @Override |
| public boolean useFullPath(Object object) |
| { |
| if (object instanceof CDOBranchPoint) |
| { |
| return true; |
| } |
| |
| return super.useFullPath(object); |
| } |
| }; |
| |
| Image leftImage = itemProvider.getImage(leftBranchPoint); |
| String leftLabel = itemProvider.getText(leftBranchPoint); |
| Image rightImage = itemProvider.getImage(rightBranchPoint); |
| String rightLabel = itemProvider.getText(rightBranchPoint); |
| itemProvider.dispose(); |
| |
| boolean leftEditable = !leftView.isReadOnly(); |
| boolean rightEditable = !rightView.isReadOnly(); |
| boolean merge = leftEditable || rightEditable; |
| if (merge) |
| { |
| leftLabel = "From " + leftLabel; |
| rightLabel = "Into " + rightLabel; |
| } |
| |
| CompareConfiguration configuration = new CompareConfiguration(); |
| configuration.setLeftImage(leftImage); |
| configuration.setLeftLabel(leftLabel); |
| configuration.setLeftEditable(leftEditable); |
| configuration.setRightImage(rightImage); |
| configuration.setRightLabel(rightLabel); |
| configuration.setRightEditable(rightEditable); |
| |
| String repositoryName = ((InternalCDOView)leftView).getRepositoryName(); |
| String title = (merge ? "Merge " : "Compare ") + repositoryName + " " + leftLabel + (merge ? " into " : " and ") |
| + rightLabel; |
| |
| Input input = new Input(rightView, configuration, comparison, editingDomain, adapterFactory); |
| input.setTitle(title); |
| return input; |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static boolean isSuppressCommit() |
| { |
| return Boolean.TRUE.equals(SUPPRESS_COMMIT.get()); |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static void setSuppressCommit(boolean suppressCommit) |
| { |
| if (suppressCommit) |
| { |
| SUPPRESS_COMMIT.set(true); |
| } |
| else |
| { |
| SUPPRESS_COMMIT.remove(); |
| } |
| } |
| |
| /** |
| * @since 4.3 |
| */ |
| public static void addDisposeRunnables(Runnable... disposeRunnables) |
| { |
| List<Runnable> list = DISPOSE_RUNNABLES.get(); |
| if (list == null) |
| { |
| list = new ArrayList<Runnable>(); |
| DISPOSE_RUNNABLES.set(list); |
| } |
| |
| list.addAll(Arrays.asList(disposeRunnables)); |
| } |
| |
| private static List<Runnable> removeDisposeRunnables() |
| { |
| List<Runnable> list = DISPOSE_RUNNABLES.get(); |
| DISPOSE_RUNNABLES.remove(); |
| return list; |
| } |
| |
| private static void runDisposeRunnables(List<Runnable> disposeRunnables) |
| { |
| if (disposeRunnables != null) |
| { |
| for (Runnable disposeRunnable : disposeRunnables) |
| { |
| try |
| { |
| disposeRunnable.run(); |
| } |
| catch (Exception ex) |
| { |
| OM.LOG.error(ex); |
| } |
| } |
| } |
| } |
| |
| private static boolean handleMerges(InternalCDOTransaction transaction, EList<Diff> differences) |
| { |
| Map<InternalCDOObject, InternalCDORevision> cleanRevisions = transaction.getCleanRevisions(); |
| Map<CDOID, CDORevisionDelta> revisionDeltas = transaction.getLastSavepoint().getRevisionDeltas2(); |
| |
| boolean unmergedConflicts = false; |
| |
| for (Diff diff : differences) |
| { |
| if (diff.getState() != DifferenceState.MERGED) |
| { |
| unmergedConflicts = true; |
| } |
| else |
| { |
| Match match = diff.getMatch(); |
| InternalCDOObject left = (InternalCDOObject)CDOUtil.getCDOObject(match.getLeft()); |
| InternalCDOObject right = (InternalCDOObject)CDOUtil.getCDOObject(match.getRight()); |
| |
| InternalCDORevision leftRevision = left.cdoRevision(); |
| cleanRevisions.put(right, leftRevision); |
| int remoteVersion = leftRevision.getVersion(); |
| |
| InternalCDORevision rightRevision = right.cdoRevision(); |
| rightRevision.setBranchPoint(leftRevision); |
| rightRevision.setVersion(remoteVersion); |
| |
| InternalCDORevisionDelta revisionDelta = (InternalCDORevisionDelta)revisionDeltas.get(rightRevision.getID()); |
| if (revisionDelta != null) |
| { |
| revisionDelta.setVersion(remoteVersion); |
| } |
| |
| transaction.removeConflict(right); |
| right.cdoInternalSetState(CDOState.DIRTY); |
| } |
| } |
| |
| return !unmergedConflicts; |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| @SuppressWarnings("restriction") |
| private static final class Input extends org.eclipse.emf.compare.ide.ui.internal.editor.ComparisonEditorInput |
| { |
| private static final Image COMPARE_IMAGE = OM.getImage("icons/compare.gif"); |
| |
| private final CDOView targetView; |
| |
| private final Comparison comparison; |
| |
| private List<Runnable> disposeRunnables; |
| |
| private boolean ok; |
| |
| private boolean suppressCommit; |
| |
| private Input(CDOView targetView, CompareConfiguration configuration, Comparison comparison, |
| ICompareEditingDomain editingDomain, AdapterFactory adapterFactory) |
| { |
| super(new org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration(configuration), |
| comparison, editingDomain, adapterFactory); |
| this.targetView = targetView; |
| this.comparison = comparison; |
| |
| suppressCommit = isSuppressCommit(); |
| SUPPRESS_COMMIT.remove(); |
| } |
| |
| private void dispose() |
| { |
| AdapterFactory adapterFactory = getAdapterFactory(); |
| if (adapterFactory instanceof ComposedAdapterFactory) |
| { |
| ComposedAdapterFactory composedAdapterFactory = (ComposedAdapterFactory)adapterFactory; |
| composedAdapterFactory.dispose(); |
| } |
| |
| runDisposeRunnables(disposeRunnables); |
| disposeRunnables = null; |
| } |
| |
| public final Comparison getComparison() |
| { |
| return comparison; |
| } |
| |
| @Override |
| public Image getTitleImage() |
| { |
| return COMPARE_IMAGE; |
| } |
| |
| public void setDisposeRunnables(List<Runnable> disposeRunnables) |
| { |
| this.disposeRunnables = disposeRunnables; |
| } |
| |
| @Override |
| public void saveChanges(IProgressMonitor monitor) throws CoreException |
| { |
| if (targetView instanceof CDOTransaction) |
| { |
| CDOTransaction transaction = (CDOTransaction)targetView; |
| if (transaction.isDirty()) |
| { |
| Collection<CDOObject> values = transaction.getNewObjects().values(); |
| if (!values.isEmpty()) |
| { |
| CDOObject[] rightObjects = values.toArray(new CDOObject[values.size()]); |
| for (CDOObject rightObject : rightObjects) |
| { |
| Match match = comparison.getMatch(rightObject); |
| if (match != null) |
| { |
| CDOObject leftObject = CDOUtil.getCDOObject(match.getLeft()); |
| CDOID id = leftObject.cdoID(); |
| |
| org.eclipse.emf.internal.cdo.transaction.CDOTransactionImpl.resurrectObject(rightObject, id); |
| } |
| } |
| } |
| |
| try |
| { |
| if (!suppressCommit) |
| { |
| transaction.commit(monitor); |
| setDirty(false); |
| } |
| } |
| catch (Exception ex) |
| { |
| OM.BUNDLE.coreException(ex); |
| } |
| } |
| } |
| } |
| |
| public boolean isOK() |
| { |
| return ok; |
| } |
| |
| @Override |
| public boolean okPressed() |
| { |
| try |
| { |
| ok = true; |
| return super.okPressed(); |
| } |
| finally |
| { |
| dispose(); |
| } |
| } |
| |
| @Override |
| public void removePropertyChangeListener(IPropertyChangeListener listener) |
| { |
| try |
| { |
| super.removePropertyChangeListener(listener); |
| } |
| finally |
| { |
| dispose(); |
| } |
| } |
| } |
| } |