From b7485bcdeca40a7ff8e97d9d001713565bcd7bf6 Mon Sep 17 00:00:00 2001 From: Christian W. Damus Date: Wed, 28 Sep 2016 11:40:12 -0400 Subject: Bug 502461: [Copy/Paste] Pasting UML-RT protocol container corrupts the model https://bugs.eclipse.org/bugs/show_bug.cgi?id=502461 Never copy the Dependency::client reference property of an InterfaceRealization in copy/paste operations. Use the Element Types Framework to set the name of a pasted element when resolving name clashes, to allow for extensible editing behaviour of DSMLs such as UML-RT (for protocol containers). Change-Id: Iddfc4f7f4d5a9a412317f7fbe5db6a6ac7563be7 --- .../META-INF/MANIFEST.MF | 3 +- .../core/org.eclipse.papyrus.infra.core/pom.xml | 2 +- .../core/internal/clipboard/CopierFactory.java | 127 +++++++++++++++++++++ .../common/commands/DefaultCopyCommand.java | 8 +- .../common/commands/DefaultPasteCommand.java | 6 +- .../META-INF/MANIFEST.MF | 2 +- .../tools/org.eclipse.papyrus.uml.tools/pom.xml | 2 +- .../org/eclipse/papyrus/uml/tools/Activator.java | 42 ++++--- .../uml/tools/commands/RenameElementCommand.java | 46 +++++++- 9 files changed, 212 insertions(+), 26 deletions(-) create mode 100644 plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/clipboard/CopierFactory.java diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF b/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF index eec2ac0e47a..ed09aea07e3 100644 --- a/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/META-INF/MANIFEST.MF @@ -3,6 +3,7 @@ Export-Package: org.eclipse.papyrus.infra.core, org.eclipse.papyrus.infra.core.clipboard, org.eclipse.papyrus.infra.core.editor, org.eclipse.papyrus.infra.core.extension, + org.eclipse.papyrus.infra.core.internal.clipboard;x-internal:=true, org.eclipse.papyrus.infra.core.internal.expressions;x-internal:=true, org.eclipse.papyrus.infra.core.internal.language;x-friends:="org.eclipse.papyrus.infra.emf", org.eclipse.papyrus.infra.core.internal.sashmodel;x-internal:=true, @@ -29,7 +30,7 @@ Require-Bundle: org.eclipse.emf.workspace;bundle-version="[1.5.0,2.0.0)", org.eclipse.core.resources;bundle-version="[3.11.0,4.0.0)";visibility:=reexport Bundle-Vendor: %providerName Bundle-ActivationPolicy: lazy -Bundle-Version: 2.1.0.qualifier +Bundle-Version: 2.1.1.qualifier Bundle-Name: %pluginName Bundle-Localization: plugin Bundle-Activator: org.eclipse.papyrus.infra.core.Activator diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/pom.xml b/plugins/infra/core/org.eclipse.papyrus.infra.core/pom.xml index 7451b14b5f9..26e9fcc3e85 100644 --- a/plugins/infra/core/org.eclipse.papyrus.infra.core/pom.xml +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/pom.xml @@ -7,6 +7,6 @@ 0.0.1-SNAPSHOT org.eclipse.papyrus.infra.core - 2.1.0-SNAPSHOT + 2.1.1-SNAPSHOT eclipse-plugin \ No newline at end of file diff --git a/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/clipboard/CopierFactory.java b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/clipboard/CopierFactory.java new file mode 100644 index 00000000000..0bbf74a1c73 --- /dev/null +++ b/plugins/infra/core/org.eclipse.papyrus.infra.core/src/org/eclipse/papyrus/infra/core/internal/clipboard/CopierFactory.java @@ -0,0 +1,127 @@ +/***************************************************************************** + * Copyright (c) 2016 Christian W. Damus 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: + * Christian W. Damus - Initial API and implementation + * + *****************************************************************************/ + +package org.eclipse.papyrus.infra.core.internal.clipboard; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.BiPredicate; +import java.util.function.Supplier; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.util.EcoreUtil.Copier; + +/** + * A factory that creates copiers for the Papyrus Clipboard. + */ +public class CopierFactory implements Supplier { + + private static List> referenceFilters = new CopyOnWriteArrayList<>(); + + private final boolean useOriginalReferences; + + /** + * The default copier factory that provides the usual EMF + * copying semantics, except with filtering of references + * as directed by {@linkplain #registerReferenceFilter(BiPredicate) registered filters}. + */ + public static CopierFactory DEFAULT = new CopierFactory(true); + + /** + * Initializes me. + * + * @param useOriginalReferences + * whether non-copied references should be used while copying + */ + public CopierFactory(boolean useOriginalReferences) { + super(); + + this.useOriginalReferences = useOriginalReferences; + } + + /** + * Queries whether non-copied references should be used while copying. + * + * @return whether to use non-copied references + */ + public boolean isUseOriginalReferences() { + return useOriginalReferences; + } + + @Override + public Copier get() { + Copier result; + + Optional> referenceFilter = getReferenceFilter(); + result = referenceFilter.map(this::createReferenceFilteringCopier) + .orElseGet(this::createBasicCopier); + + return result; + } + + /** + * Obtains a predicate that accepts any reference not matching + * any of our registered filters (a {@code not} of the collective + * {@code or} of our filters). + * + * @return the optional reference filter + */ + private Optional> getReferenceFilter() { + return referenceFilters.stream() + .reduce(BiPredicate::or) + .map(BiPredicate::negate); + } + + private Copier createReferenceFilteringCopier(BiPredicate referencePredicate) { + return new ReferenceFilteringCopier(true, isUseOriginalReferences(), referencePredicate); + } + + private Copier createBasicCopier() { + return new Copier(true, isUseOriginalReferences()); + } + + /** + * Registers a predicate that matches references to be excluded from copy operations. + * + * @param filter + * a predicate that matches a reference to be excluded for a given owner object + */ + public static void registerReferenceFilter(BiPredicate filter) { + referenceFilters.add(filter); + } + + // + // Nested types + // + + private static class ReferenceFilteringCopier extends Copier { + private static final long serialVersionUID = 1L; + + private final BiPredicate referencePredicate; + + ReferenceFilteringCopier(boolean resolveReferences, boolean useOriginalReferences, BiPredicate referencePredicate) { + super(resolveReferences, useOriginalReferences); + + this.referencePredicate = referencePredicate; + } + + @Override + protected void copyReference(EReference eReference, EObject eObject, EObject copyEObject) { + if (referencePredicate.test(eReference, eObject)) { + super.copyReference(eReference, eObject, copyEObject); + } + } + } +} diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/commands/DefaultCopyCommand.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/commands/DefaultCopyCommand.java index 6703095a1a1..7c7abf28f38 100644 --- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/commands/DefaultCopyCommand.java +++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/commands/DefaultCopyCommand.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014 CEA LIST. + * Copyright (c) 2014, 2016 CEA LIST, Christian W. Damus, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -8,6 +8,7 @@ * * Contributors: * Benoit Maggi (CEA LIST) benoit.maggi@cea.fr - Initial API and implementation + * Christian W. Damus - bug 502461 *****************************************************************************/ package org.eclipse.papyrus.infra.gmfdiag.common.commands; @@ -20,6 +21,7 @@ import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.edit.command.AbstractOverrideableCommand; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.papyrus.infra.core.clipboard.PapyrusClipboard; +import org.eclipse.papyrus.infra.core.internal.clipboard.CopierFactory; import org.eclipse.papyrus.infra.gmfdiag.common.Activator; import org.eclipse.papyrus.infra.gmfdiag.common.preferences.PastePreferencesPage; @@ -47,8 +49,8 @@ public class DefaultCopyCommand extends AbstractOverrideableCommand implements N public DefaultCopyCommand(EditingDomain domain, PapyrusClipboard papyrusClipboard, Collection pObjectsToPutInClipboard) { super(domain); objectsToPutInClipboard = new ArrayList(); - Boolean keepReferences = Activator.getInstance().getPreferenceStore().getBoolean(PastePreferencesPage.KEEP_EXTERNAL_REFERENCES); - EcoreUtil.Copier copier = new EcoreUtil.Copier(Boolean.TRUE, keepReferences); + boolean keepReferences = Activator.getInstance().getPreferenceStore().getBoolean(PastePreferencesPage.KEEP_EXTERNAL_REFERENCES); + EcoreUtil.Copier copier = new CopierFactory(keepReferences).get(); copier.copyAll(pObjectsToPutInClipboard); copier.copyReferences(); papyrusClipboard.addAllInternalCopyInClipboard(copier); diff --git a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/commands/DefaultPasteCommand.java b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/commands/DefaultPasteCommand.java index d07f91911f3..4fd0ba49aef 100644 --- a/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/commands/DefaultPasteCommand.java +++ b/plugins/infra/gmfdiag/org.eclipse.papyrus.infra.gmfdiag.common/src/org/eclipse/papyrus/infra/gmfdiag/common/commands/DefaultPasteCommand.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014 CEA LIST. + * Copyright (c) 2014, 2016 CEA LIST, Christian W. Damus, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -8,6 +8,7 @@ * * Contributors: * Benoit Maggi (CEA LIST) benoit.maggi@cea.fr - Initial API and implementation + * Christian W. Damus - bug 502461 *****************************************************************************/ package org.eclipse.papyrus.infra.gmfdiag.common.commands; @@ -28,6 +29,7 @@ import org.eclipse.gmf.runtime.common.core.command.CompositeCommand; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.emf.type.core.requests.MoveRequest; import org.eclipse.papyrus.infra.core.clipboard.PapyrusClipboard; +import org.eclipse.papyrus.infra.core.internal.clipboard.CopierFactory; import org.eclipse.papyrus.infra.gmfdiag.common.Activator; import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils; import org.eclipse.papyrus.infra.services.edit.service.IElementEditService; @@ -69,7 +71,7 @@ public class DefaultPasteCommand extends AbstractCommand { List rootElementToPaste = EcoreUtil.filterDescendants(eobjectsTopaste); // Copy all eObjects (inspired from PasteFromClipboardCommand) - EcoreUtil.Copier copier = new EcoreUtil.Copier(); + EcoreUtil.Copier copier = CopierFactory.DEFAULT.get(); copier.copyAll(rootElementToPaste); copier.copyReferences(); Map duplicatedObjects = new HashMap(); diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/META-INF/MANIFEST.MF b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/META-INF/MANIFEST.MF index d455afd03cc..7b4b4e0129c 100644 --- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/META-INF/MANIFEST.MF +++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/META-INF/MANIFEST.MF @@ -25,7 +25,7 @@ Require-Bundle: org.eclipse.uml2.uml;bundle-version="[5.2.0,6.0.0)";visibility:= org.eclipse.papyrus.infra.services.edit.ui;bundle-version="[2.0.0,3.0.0)";visibility:=reexport Bundle-Vendor: Eclipse Modeling Project Bundle-ActivationPolicy: lazy -Bundle-Version: 2.0.1.qualifier +Bundle-Version: 2.0.2.qualifier Bundle-Name: UML Tools Bundle-Activator: org.eclipse.papyrus.uml.tools.Activator Bundle-ManifestVersion: 2 diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/pom.xml b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/pom.xml index 4866ac943e0..8aefb1bfbd0 100644 --- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/pom.xml +++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/pom.xml @@ -7,6 +7,6 @@ 0.0.1-SNAPSHOT org.eclipse.papyrus.uml.tools - 2.0.1-SNAPSHOT + 2.0.2-SNAPSHOT eclipse-plugin \ No newline at end of file diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/Activator.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/Activator.java index 26b5d8d7420..04ef29edffe 100644 --- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/Activator.java +++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/Activator.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014, 2015 CEA LIST, Christian W. Damus, and others. + * Copyright (c) 2014, 2016 CEA LIST, Christian W. Damus, and others. * * * All rights reserved. This program and the accompanying materials @@ -10,7 +10,7 @@ * Contributors: * Patrick Tessier (CEA LIST) - Initial API and implementation * Christian W. Damus (CEA) - bug 425270 - * Christian W. Damus - bug 469464 + * Christian W. Damus - bugs 469464, 502461 * /*****************************************************************************/ package org.eclipse.papyrus.uml.tools; @@ -21,9 +21,11 @@ import java.util.List; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.edit.provider.ComposedAdapterFactory; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.papyrus.infra.core.internal.clipboard.CopierFactory; import org.eclipse.papyrus.infra.core.log.LogHelper; import org.eclipse.papyrus.uml.tools.utils.ElementUtil; import org.eclipse.papyrus.uml.tools.utils.ImageUtil; @@ -31,9 +33,14 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.uml2.uml.Element; +import org.eclipse.uml2.uml.InterfaceRealization; import org.eclipse.uml2.uml.Stereotype; +import org.eclipse.uml2.uml.UMLPackage; import org.osgi.framework.BundleContext; +import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.SetMultimap; + /** * The activator class controls the plug-in life cycle */ @@ -62,24 +69,16 @@ public class Activator extends AbstractUIPlugin { public Activator() { } - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) - */ @Override public void start(BundleContext context) throws Exception { super.start(context); plugin = this; log = new LogHelper(this); adapterFactory = createAdapterFactory(); + + ensureSensibleCopying(); } - /* - * (non-Javadoc) - * - * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) - */ @Override public void stop(BundleContext context) throws Exception { adapterFactory.dispose(); @@ -106,6 +105,24 @@ public class Activator extends AbstractUIPlugin { return adapterFactory; } + private static void ensureSensibleCopying() { + // Copying the 'client' reference results in multiple redundant values, + // which breaks the XMI deserialization (cf. bug 479425) + SetMultimap doNotCopy = ImmutableSetMultimap. builder() + .put(UMLPackage.Literals.INTERFACE_REALIZATION, UMLPackage.Literals.DEPENDENCY__CLIENT) + .build(); + + CopierFactory.registerReferenceFilter((reference, owner) -> { + boolean result = false; + + if (owner instanceof InterfaceRealization) { + result = doNotCopy.containsEntry(UMLPackage.Literals.INTERFACE_REALIZATION, reference); + } + + return result; + }); + } + @@ -262,5 +279,4 @@ public class Activator extends AbstractUIPlugin { return image; } - } diff --git a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/commands/RenameElementCommand.java b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/commands/RenameElementCommand.java index ee760f9c3e5..968479a39d1 100644 --- a/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/commands/RenameElementCommand.java +++ b/plugins/uml/tools/org.eclipse.papyrus.uml.tools/src/org/eclipse/papyrus/uml/tools/commands/RenameElementCommand.java @@ -1,5 +1,5 @@ /***************************************************************************** - * Copyright (c) 2014 CEA LIST. + * Copyright (c) 2014, 2016 CEA LIST, Christian W. Damus, and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -8,21 +8,32 @@ * * Contributors: * Benoit Maggi (CEA LIST) benoit.maggi@cea.fr - Initial API and implementation + * Christian W. Damus - bug 502461 *****************************************************************************/ package org.eclipse.papyrus.uml.tools.commands; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.emf.transaction.RecordingCommand; import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.emf.type.core.requests.SetRequest; +import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils; +import org.eclipse.papyrus.infra.services.edit.service.IElementEditService; +import org.eclipse.papyrus.uml.tools.Activator; import org.eclipse.uml2.uml.NamedElement; +import org.eclipse.uml2.uml.UMLPackage; /** * A Command to apply a rename on a UML Element */ public class RenameElementCommand extends RecordingCommand { - private NamedElement element; + private final TransactionalEditingDomain domain; - private String name; + private final NamedElement element; + + private final String name; /** * @@ -35,6 +46,8 @@ public class RenameElementCommand extends RecordingCommand { */ public RenameElementCommand(TransactionalEditingDomain domain, NamedElement namedElement, String name) { super(domain); + + this.domain = domain; this.element = namedElement; this.name = name; } @@ -42,7 +55,32 @@ public class RenameElementCommand extends RecordingCommand { @Override protected void doExecute() { - element.setName(name); + ICommand rename = null; + + // Try first to use the element edit service + IElementEditService edit = ElementEditServiceUtils.getCommandProvider(element); + if (edit != null) { + rename = edit.getEditCommand(new SetRequest(domain, element, UMLPackage.Literals.NAMED_ELEMENT__NAME, name)); + } + + try { + if ((rename != null) && rename.canExecute()) { + try { + rename.execute(new NullProgressMonitor(), null); + } catch (ExecutionException e) { + // Plan B + element.setName(name); + Activator.log.error(e); + } + } else { + // Plan B + element.setName(name); + } + } finally { + if (rename != null) { + rename.dispose(); + } + } } } -- cgit v1.2.3