/******************************************************************************* * Copyright (c) 2016 EclipseSource Muenchen GmbH 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: * Stefan Dirix - initial API and implementation *******************************************************************************/ package org.eclipse.emf.compare.ide.ui.internal.handler; import com.google.common.base.Function; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import java.text.MessageFormat; import java.util.Collection; import java.util.HashSet; import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.CompareEditorInput; import org.eclipse.compare.CompareUI; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.resources.IStorage; import org.eclipse.core.runtime.IAdapterManager; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.emf.common.command.CommandStack; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.compare.EMFCompare; import org.eclipse.emf.compare.EMFCompare.Builder; import org.eclipse.emf.compare.domain.ICompareEditingDomain; import org.eclipse.emf.compare.domain.impl.EMFCompareEditingDomain; import org.eclipse.emf.compare.ide.ui.internal.EMFCompareIDEUIMessages; import org.eclipse.emf.compare.ide.ui.internal.configuration.EMFCompareConfiguration; import org.eclipse.emf.compare.ide.ui.internal.editor.ComparisonScopeEditorInput; import org.eclipse.emf.compare.ide.ui.internal.logical.ComparisonScopeBuilder; import org.eclipse.emf.compare.ide.ui.logical.SynchronizationModel; import org.eclipse.emf.compare.ide.ui.source.IEMFComparisonSource; import org.eclipse.emf.compare.ide.utils.StorageTraversal; import org.eclipse.emf.compare.rcp.EMFCompareRCPPlugin; import org.eclipse.emf.compare.rcp.internal.extension.impl.EMFCompareBuilderConfigurator; import org.eclipse.emf.compare.scope.IComparisonScope; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.emf.edit.domain.IEditingDomainProvider; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.emf.edit.provider.IDisposable; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.handlers.HandlerUtil; /** * Handles the comparison between selections adapting to {@link IEMFComparisonSource}s. * * @author Stefan Dirix <sdirix@eclipsesource.com> */ public class EMFComparisonSourceActionHandler extends AbstractHandler { /** * {@inheritDoc} */ public Object execute(ExecutionEvent event) throws ExecutionException { final IStructuredSelection selection = (IStructuredSelection)HandlerUtil.getCurrentSelection(event); final IEMFComparisonSource[] sources = adaptSelection(selection.toList()); final AdapterFactory adapterFactory = new ComposedAdapterFactory(EMFCompareRCPPlugin.getDefault() .createFilteredAdapterFactoryRegistry(Maps.newLinkedHashMap())); final IEMFComparisonSource leftSource; final IEMFComparisonSource rightSource; final IEMFComparisonSource originSource; if (sources.length == 3) { final Shell shell = HandlerUtil.getActiveShell(event); final SelectAncestorDialog<IEMFComparisonSource> dialog = new SelectAncestorDialog<IEMFComparisonSource>( shell, adapterFactory, sources); if (dialog.open() == Window.CANCEL) { return null; } else { leftSource = dialog.leftElement; rightSource = dialog.rightElement; originSource = dialog.originElement; } } else if (sources.length == 2) { leftSource = sources[0]; rightSource = sources[1]; originSource = null; } else { return null; } final StorageTraversal left = getStorageTraversal(leftSource); final StorageTraversal right = getStorageTraversal(rightSource); final StorageTraversal origin = getStorageTraversal(originSource); if (left.getStorages().size() == 0 && right.getStorages().size() == 0 && origin.getStorages().size() == 0) { MessageDialog.openError(HandlerUtil.getActiveShell(event), EMFCompareIDEUIMessages .getString("EMFComparisonSourceActionHandler_NoResourcesFound_Title"), //$NON-NLS-1$ EMFCompareIDEUIMessages .getString("EMFComparisonSourceActionHandler_NoResourcesFound_Message")); //$NON-NLS-1$ return null; } final SynchronizationModel model = new SynchronizationModel(left, right, origin); final IComparisonScope scope = ComparisonScopeBuilder.create(model, new NullProgressMonitor()); final IWorkbenchPart activePart = HandlerUtil.getActivePart(event); final CommandStack commandStack = getCommandStack(activePart); String inputTitle = MessageFormat.format( EMFCompareIDEUIMessages.getString("EMFComparisonSourceActionHandler_InputTitle_Base"), //$NON-NLS-1$ leftSource.getName(), rightSource.getName()); if (originSource != null) { inputTitle += MessageFormat.format( ' ' + EMFCompareIDEUIMessages .getString("EMFComparisonSourceActionHandler_InputTitle_Origin"), //$NON-NLS-1$ originSource.getName()); } final CompareEditorInput input = createCompareEditorInput(commandStack, adapterFactory, scope, inputTitle); CompareUI.openCompareEditor(input); return null; } private IEMFComparisonSource[] adaptSelection(Collection<?> selection) { final IAdapterManager adapterManager = Platform.getAdapterManager(); final Iterable<IEMFComparisonSource> sources = Iterables.transform(selection, new Function<Object, IEMFComparisonSource>() { public IEMFComparisonSource apply(Object input) { return (IEMFComparisonSource)adapterManager.loadAdapter(input, IEMFComparisonSource.class.getName()); } }); return Iterables.toArray(sources, IEMFComparisonSource.class); } /** * Determines the {@link StorageTraversal} for the given {@code source}. * * @param source * The {@link IEMFComparisonSource} which provides the {@link StorageTraversal}. Can be * {@code null}. * @return The {@link StorageTraversal} for the given {@code source}. */ private StorageTraversal getStorageTraversal(IEMFComparisonSource source) { if (source == null) { return new StorageTraversal(new HashSet<IStorage>()); } return source.getStorageTraversal(); } /** * Creates the comparison input. * * @param commandStack * The {@link CommandStack} which is used within the input. Can be {@code} null. * @param adapterFactory * The {@link AdapterFactory} which shall be used in the created input. * @param scope * The {@IComparisonScope} which shall be used in the created input. * @param inputTitle * The title for the created input. * @return The created CompareEditorInput. */ private CompareEditorInput createCompareEditorInput(final CommandStack commandStack, final AdapterFactory adapterFactory, final IComparisonScope scope, final String inputTitle) { final ICompareEditingDomain editingDomain = createEMFCompareEditingDomain(commandStack, scope.getLeft(), scope.getRight(), scope.getOrigin()); final EMFCompareConfiguration configuration = new EMFCompareConfiguration(new CompareConfiguration()); final Builder builder = EMFCompare.builder(); final EMFCompareBuilderConfigurator engineProvider = EMFCompareBuilderConfigurator.createDefault(); engineProvider.configure(builder); final EMFCompare comparator = builder.build(); final CompareEditorInput input = new ComparisonScopeEditorInput(configuration, editingDomain, adapterFactory, comparator, scope) { /** * {@inheritDoc} * * @see org.eclipse.compare.CompareEditorInput#handleDispose() */ @Override protected void handleDispose() { super.handleDispose(); if (editingDomain instanceof IDisposable) { ((IDisposable)editingDomain).dispose(); } } }; input.setTitle(inputTitle); configuration.setContainer(input); return input; } /** * Checks if the given {@code object} can provide an {@link EditingDomain} with a command stack. * * @param object * The object which is checked for an {@link IEditingDomainProvider} which provides a command * stack. * @return The provided {@link CommandStack} or {@code null} otherwise. */ private CommandStack getCommandStack(final Object object) { if (object instanceof IEditingDomainProvider) { final IEditingDomainProvider provider = (IEditingDomainProvider)object; final EditingDomain domain = provider.getEditingDomain(); if (domain != null) { return domain.getCommandStack(); } } return null; } /** * Creates the {@link EMFCompareEditingDomain} for the given arguments. * * @param delegatingCommandStack * Used in the newly created {@link EMFCompareEditingDomain}. Can be null. * @param left * The "left" part of the comparison. * @param right * The "right" part of the comparison. * @param origin * The "origin" part of the comparison. * @return The newly created {@link EMFCompareEditingDomain}. */ private ICompareEditingDomain createEMFCompareEditingDomain(final CommandStack delegatingCommandStack, final Notifier left, final Notifier right, Notifier origin) { if (delegatingCommandStack == null) { return EMFCompareEditingDomain.create(left, right, origin); } else { return EMFCompareEditingDomain.create(left, right, origin, delegatingCommandStack); } } }