Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2021-02-27 13:38:11 +0000
committerPatrick Tessier2021-04-27 11:59:34 +0000
commit9ed684ebd9a9cc16bf77cb3af66fc0fd70e250f2 (patch)
treed7185ba4c0012b0fcf0bbb902d97a0f1ec03e3f3
parent910ca5b19fbe33b45156e1eeb314ec9478c0b63c (diff)
downloadorg.eclipse.papyrus-9ed684ebd9a9cc16bf77cb3af66fc0fd70e250f2.tar.gz
org.eclipse.papyrus-9ed684ebd9a9cc16bf77cb3af66fc0fd70e250f2.tar.xz
org.eclipse.papyrus-9ed684ebd9a9cc16bf77cb3af66fc0fd70e250f2.zip
Bug 571561: [Element Types] Simple rule configuration refactorings
- add refactoring actions for rule-based element type rules - account for non-containment of NotRuleConfiguration::composedRule (bug 571560) Change-Id: Ie32f0524429801ca0ccbc6b91863403161c74f7e Signed-off-by: Christian W. Damus <give.a.damus@gmail.com>
-rw-r--r--plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/META-INF/MANIFEST.MF3
-rw-r--r--plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/plugin.xml10
-rw-r--r--plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/src/org/eclipse/papyrus/infra/types/rulebased/internal/expressions/RuleConfigurationPropertyTester.java176
-rw-r--r--plugins/infra/types/org.eclipse.papyrus.infra.types.ui/META-INF/MANIFEST.MF4
-rw-r--r--plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.properties20
-rw-r--r--plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.xml224
-rw-r--r--plugins/infra/types/org.eclipse.papyrus.infra.types.ui/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/RuleRefactoringHandler.java209
-rw-r--r--tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java18
-rw-r--r--tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/.classpath12
-rw-r--r--tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/META-INF/MANIFEST.MF9
-rw-r--r--tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/org.eclipse.papyrus.infra.types.ui.tests.launch79
-rw-r--r--tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/tests/RuleRefactoringHandlerTest.java396
-rw-r--r--tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/tests/test.elementtypesconfigurations8
-rw-r--r--tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/ui/tests/AllTests.java14
14 files changed, 1127 insertions, 55 deletions
diff --git a/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/META-INF/MANIFEST.MF b/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/META-INF/MANIFEST.MF
index cf80bc9ca8a..d5da35ebf58 100644
--- a/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/META-INF/MANIFEST.MF
+++ b/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/META-INF/MANIFEST.MF
@@ -6,10 +6,13 @@ Require-Bundle: org.eclipse.gmf.runtime.emf.type.core;bundle-version="[1.9.0,2.0
org.eclipse.papyrus.infra.types.edit;bundle-version="[5.0.0,6.0.0)",
org.eclipse.uml2.common.edit;bundle-version="[2.5.0,3.0.0)",
org.eclipse.uml2.types;bundle-version="[2.5.0,3.0.0)",
+ org.eclipse.core.expressions;bundle-version="[3.7.100,4.0.0)",
+ org.eclipse.papyrus.infra.emf;bundle-version="[4.0.0,5.0.0)",
org.eclipse.uml2.uml;bundle-version="[5.5.0,6.0.0)"
Export-Package: org.eclipse.papyrus.infra.types.rulebased,
org.eclipse.papyrus.infra.types.rulebased.core,
org.eclipse.papyrus.infra.types.rulebased.impl,
+ org.eclipse.papyrus.infra.types.rulebased.internal.expressions;x-internal:=true,
org.eclipse.papyrus.infra.types.rulebased.provider,
org.eclipse.papyrus.infra.types.rulebased.util
Bundle-Vendor: %providerName
diff --git a/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/plugin.xml b/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/plugin.xml
index a3887ad26ca..333682f8286 100644
--- a/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/plugin.xml
+++ b/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/plugin.xml
@@ -45,4 +45,14 @@
uri="http://www.eclipse.org/papyrus/infra/elementtypesconfigurations/1.2"
class="org.eclipse.papyrus.infra.types.rulebased.provider.RuleBasedItemProviderAdapterFactory$ElementTypesConfigurationsChildCreationExtender"/>
</extension>
+ <extension
+ point="org.eclipse.core.expressions.propertyTesters">
+ <propertyTester
+ class="org.eclipse.papyrus.infra.types.rulebased.internal.expressions.RuleConfigurationPropertyTester"
+ id="org.eclipse.papyrus.infra.types.rulebased.propertyTester"
+ namespace="org.eclipse.papyrus.infra.types.rulebased"
+ properties="container,operandCount"
+ type="org.eclipse.papyrus.infra.types.rulebased.RuleConfiguration">
+ </propertyTester>
+ </extension>
</plugin>
diff --git a/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/src/org/eclipse/papyrus/infra/types/rulebased/internal/expressions/RuleConfigurationPropertyTester.java b/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/src/org/eclipse/papyrus/infra/types/rulebased/internal/expressions/RuleConfigurationPropertyTester.java
new file mode 100644
index 00000000000..9daa9787495
--- /dev/null
+++ b/plugins/infra/types/org.eclipse.papyrus.infra.types.rulebased/src/org/eclipse/papyrus/infra/types/rulebased/internal/expressions/RuleConfigurationPropertyTester.java
@@ -0,0 +1,176 @@
+/*****************************************************************************
+ * Copyright (c) 2021 Christian W. Damus, CEA LIST, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.types.rulebased.internal.expressions;
+
+import static java.util.function.Predicate.not;
+
+import java.util.Objects;
+import java.util.function.IntPredicate;
+
+import org.eclipse.core.expressions.PropertyTester;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EPackage;
+import org.eclipse.emf.ecore.EcorePackage;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
+import org.eclipse.papyrus.infra.types.rulebased.CompositeRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.NotRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.RuleConfiguration;
+
+/**
+ * Implementation of properties in XML enablement expressions for rule configurations.
+ * Supported properties are:
+ *
+ * <dl>
+ * <dt>container</dt>
+ * <dd>the EClass of the rule's container. No expected value indicates that the object is
+ * expected not to have a container (i.e., to be a root object)</dd>
+ * <dt>operandCount</dt>
+ * <dd>the number of operands the rule has if it is a {@link CompositeRuleConfiguration}
+ * or a {@link NotRuleConfiguration}, otherwise zero</dd>
+ * </dl>
+ */
+public class RuleConfigurationPropertyTester extends PropertyTester {
+
+ private static final String CONTAINER = "container"; //$NON-NLS-1$
+ private static final String OPERAND_COUNT = "operandCount"; //$NON-NLS-1$
+
+ @Override
+ public boolean test(Object receiver, String property, Object[] args, Object expectedValue) {
+ boolean result = false;
+ RuleConfiguration rule = asRuleConfiguration(receiver);
+
+ if (rule != null) {
+ switch (property) {
+ case CONTAINER:
+ result = testContainer(rule, asEClass(rule, expectedValue));
+ break;
+ case OPERAND_COUNT:
+ result = testOperandCount(rule, asIntPredicate(expectedValue));
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ boolean testContainer(RuleConfiguration rule, EClass expectedContainer) {
+ EObject container = rule.eContainer();
+
+ return expectedContainer == null
+ ? container == null
+ : expectedContainer.isInstance(container);
+ }
+
+ boolean testOperandCount(RuleConfiguration rule, IntPredicate operandCountPredicate) {
+ int operandCount = 0;
+
+ if (rule instanceof NotRuleConfiguration) {
+ operandCount = ((NotRuleConfiguration) rule).getComposedRule() != null ? 1 : 0;
+ } else if (rule instanceof CompositeRuleConfiguration) {
+ operandCount = ((CompositeRuleConfiguration) rule).getComposedRules().size();
+ }
+
+ return operandCountPredicate.test(operandCount);
+ }
+
+ private RuleConfiguration asRuleConfiguration(Object object) {
+ EObject eObject = EMFHelper.getEObject(object);
+ return eObject instanceof RuleConfiguration ? (RuleConfiguration) eObject : null;
+ }
+
+ private EClass asEClass(EObject context, Object classValue) {
+ EClass result = null;
+
+ if (classValue != null) {
+ // Let something vague like "*" just denote that there is some container
+ result = EcorePackage.Literals.EOBJECT;
+
+ String classSpec = String.valueOf(classValue);
+ int fragmentSeparator = classSpec.indexOf('#');
+ if (fragmentSeparator >= 0) {
+ // It looks like an EClass URI
+ URI uri = URI.createURI(classSpec);
+ result = getEClass(context, uri);
+ } else {
+ // Take it as the instance class name of an EClass in the context package
+ result = findEClass(context, classSpec);
+ }
+ }
+
+ return result;
+ }
+
+ private IntPredicate asIntPredicate(Object expression) {
+ IntPredicate result;
+
+ if (expression == null) {
+ // Test is just whether the value is positive
+ result = i -> i > 0;
+ } else {
+ // TODO: For now, we are only supporting literal integers, not e.g. "> 1"
+ String predicateSpec = String.valueOf(expression);
+ result = Integer.valueOf(predicateSpec)::equals;
+ }
+
+ return result;
+ }
+
+ private EClass getEClass(EObject context, URI uri) {
+ EObject resolved = null;
+
+ ResourceSet rset = EMFHelper.getResourceSet(context);
+ if (rset != null) {
+ resolved = rset.getEObject(uri, true);
+ } else {
+ EPackage package_ = EPackage.Registry.INSTANCE.getEPackage(uri.trimFragment().toString());
+ Resource resource = package_ != null ? package_.eResource() : null;
+ if (resource != null) {
+ resolved = resource.getEObject(uri.fragment());
+ }
+ }
+
+ return (resolved instanceof EClass) ? (EClass) resolved : null;
+ }
+
+ private EClass findEClass(EObject context, String instanceClassName) {
+ EClass result = getEClass(context.eClass().getEPackage(), instanceClassName);
+
+ if (result == null) {
+ result = context.eClass().getEAllSuperTypes().stream()
+ .map(EClassifier::getEPackage)
+ .filter(not(context.eClass().getEPackage()::equals))
+ .distinct()
+ .map(package_ -> getEClass(package_, instanceClassName))
+ .filter(Objects::nonNull)
+ .findFirst().orElse(null);
+ }
+
+ return result;
+ }
+
+ private EClass getEClass(EPackage package_, String instanceClassName) {
+ return package_.getEClassifiers().stream()
+ .filter(c -> instanceClassName.equals(c.getInstanceClassName()))
+ .filter(EClass.class::isInstance).map(EClass.class::cast)
+ .findAny().orElse(null);
+ }
+
+}
diff --git a/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/META-INF/MANIFEST.MF b/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/META-INF/MANIFEST.MF
index df347a01d9a..276ed241c6d 100644
--- a/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/META-INF/MANIFEST.MF
+++ b/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/META-INF/MANIFEST.MF
@@ -4,7 +4,9 @@ Require-Bundle: org.eclipse.gmf.runtime.emf.type.core;bundle-version="[1.9.0,2.0
org.eclipse.papyrus.infra.types;bundle-version="[5.0.0,6.0.0)",
org.eclipse.papyrus.infra.types.core;bundle-version="[5.0.0,6.0.0)",
org.eclipse.ui;bundle-version="[3.117.0,4.0.0)",
- org.eclipse.uml2.types;bundle-version="[2.5.0,3.0.0)"
+ org.eclipse.uml2.types;bundle-version="[2.5.0,3.0.0)",
+ org.eclipse.papyrus.infra.types.rulebased;bundle-version="[5.0.0,6.0.0)",
+ org.eclipse.papyrus.infra.emf;bundle-version="[4.0.0,5.0.0)"
Export-Package: org.eclipse.papyrus.infra.types.core.internal.ui;x-internal:=true,
org.eclipse.papyrus.infra.types.core.internal.ui.handlers;x-internal:=true
Bundle-Vendor: %providerName
diff --git a/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.properties b/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.properties
index 7cd40c2f146..14b215ac97e 100644
--- a/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.properties
+++ b/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2010, 2016 CEA LIST, Christian W. Damus, and others.
+# Copyright (c) 2010, 2021 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 2.0
@@ -10,8 +10,24 @@
#
# Contributors:
# CEA LIST - initial API and implementation
-# Christian W. Damus - bug 485220
+# Christian W. Damus - bugs 571561, 485220, 570542
#
###############################################################################
pluginName=Papyrus Element Types Configurations UI
providerName=Eclipse Modeling Project
+
+command.deploy.name = Deploy ElementTypes Set configuration
+command.disable.name = Disable ElementTypes Set configuration
+command.negate.description = Negate the selected rule configuration
+command.negate.name = Negate Rule
+command.pullUp.description = Replace the selected rule configuration with its sole operand
+command.pullUp.name = Pull Up Operand Rule
+command.addToAnd.description = Wrap the selected rule configuration in a new 'and' rule
+command.addToAnd.name = Add Rule to New And
+command.addToOr.description = Wrap the selected rule configuration in a new 'or' rule
+command.addToOr.name = Add Rule to New Or
+menu.refactor.label = Refactor
+command.negate.label = Negate Rule
+command.addToAnd.label = Add Rule to New And
+command.addToOr.label = Add Rule to New Or
+command.pullUp.label = Pull Up Operand Rule
diff --git a/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.xml b/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.xml
index 0780441a2f3..fa1d4c578bb 100644
--- a/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.xml
+++ b/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/plugin.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<!--
- Copyright (c) 2014, 2016 CEA LIST, Christian W. Damus, and others.
+ Copyright (c) 2014, 2021 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 2.0
@@ -12,7 +12,7 @@
Contributors:
CEA LIST - Initial API and implementation
- Christian W. Damus - bug 485220
+ Christian W. Damus - bug 571561, 485220, 570542
-->
<plugin>
@@ -77,12 +77,228 @@
<command
defaultHandler="org.eclipse.papyrus.infra.types.core.internal.ui.handlers.DeployElementTypeSetConfigurationHandler"
id="org.eclipse.papyrus.infra.types.ui.deploy"
- name="Deploy ElementTypes Set configuration">
+ name="%command.deploy.name">
</command>
<command
defaultHandler="org.eclipse.papyrus.infra.types.core.internal.ui.handlers.UndeployElementTypeSetConfigurationHandler"
id="org.eclipse.papyrus.infra.types.ui.undeploy"
- name="Disable ElementTypes Set configuration">
+ name="%command.disable.name">
</command>
</extension>
+
+ <extension
+ point="org.eclipse.ui.commands">
+ <command
+ description="%command.negate.description"
+ id="org.eclipse.papyrus.infra.types.ui.commands.negateRule"
+ name="%command.negate.name">
+ </command>
+ <command
+ description="%command.pullUp.description"
+ id="org.eclipse.papyrus.infra.types.ui.commands.pullUpOperandRule"
+ name="%command.pullUp.name">
+ </command>
+ <command
+ description="%command.addToAnd.description"
+ id="org.eclipse.papyrus.infra.types.ui.commands.addToAndRule"
+ name="%command.addToAnd.name">
+ </command>
+ <command
+ description="%command.addToOr.description"
+ id="org.eclipse.papyrus.infra.types.ui.commands.addToOrRule"
+ name="%command.addToOr.name">
+ </command>
+ </extension>
+ <extension
+ point="org.eclipse.ui.handlers">
+ <handler
+ commandId="org.eclipse.papyrus.infra.types.ui.commands.negateRule">
+ <class class="org.eclipse.papyrus.infra.types.core.internal.ui.handlers.RuleRefactoringHandler">
+ <parameter
+ name="operation"
+ value="NEGATE">
+ </parameter>
+ </class>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <and>
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false"
+ operator="or">
+ <adapt
+ type="org.eclipse.papyrus.infra.types.rulebased.RuleConfiguration">
+ <or>
+ <not>
+ <instanceof
+ value="org.eclipse.papyrus.infra.types.rulebased.NotRuleConfiguration">
+ </instanceof>
+ </not>
+ <test
+ property="org.eclipse.papyrus.infra.types.rulebased.operandCount"
+ value="1">
+ </test>
+ </or>
+ </adapt>
+ </iterate>
+ </and>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ commandId="org.eclipse.papyrus.infra.types.ui.commands.pullUpOperandRule">
+ <class class="org.eclipse.papyrus.infra.types.core.internal.ui.handlers.RuleRefactoringHandler">
+ <parameter
+ name="operation"
+ value="PULL_UP_OPERAND">
+ </parameter>
+ </class>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <and>
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false"
+ operator="or">
+ <adapt
+ type="org.eclipse.papyrus.infra.types.rulebased.RuleConfiguration">
+ <and>
+ <!-- In the case of the NotRuleConfiguration, the Negate refactoring does this. -->
+ <instanceof
+ value="org.eclipse.papyrus.infra.types.rulebased.CompositeRuleConfiguration">
+ </instanceof>
+ <test
+ property="org.eclipse.papyrus.infra.types.rulebased.operandCount"
+ value="1">
+ </test>
+ </and>
+ </adapt>
+ </iterate>
+ </and>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ commandId="org.eclipse.papyrus.infra.types.ui.commands.addToAndRule">
+ <class class="org.eclipse.papyrus.infra.types.core.internal.ui.handlers.RuleRefactoringHandler">
+ <parameter
+ name="operation"
+ value="ADD_TO_AND">
+ </parameter>
+ </class>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <and>
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false"
+ operator="or">
+ <adapt
+ type="org.eclipse.papyrus.infra.types.rulebased.RuleConfiguration">
+ <not>
+ <test
+ property="org.eclipse.papyrus.infra.types.rulebased.container"
+ value="org.eclipse.papyrus.infra.types.rulebased.AndRuleConfiguration">
+ </test>
+ </not>
+ </adapt>
+ </iterate>
+ </and>
+ </with>
+ </enabledWhen>
+ </handler>
+ <handler
+ commandId="org.eclipse.papyrus.infra.types.ui.commands.addToOrRule">
+ <class class="org.eclipse.papyrus.infra.types.core.internal.ui.handlers.RuleRefactoringHandler">
+ <parameter
+ name="operation"
+ value="ADD_TO_OR">
+ </parameter>
+ </class>
+ <enabledWhen>
+ <with
+ variable="selection">
+ <and>
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false"
+ operator="or">
+ <adapt
+ type="org.eclipse.papyrus.infra.types.rulebased.RuleConfiguration">
+ <not>
+ <test
+ property="org.eclipse.papyrus.infra.types.rulebased.container"
+ value="org.eclipse.papyrus.infra.types.rulebased.OrRuleConfiguration">
+ </test>
+ </not>
+ </adapt>
+ </iterate>
+ </and>
+ </with>
+ </enabledWhen>
+ </handler>
+ </extension>
+ <extension
+ point="org.eclipse.ui.menus">
+ <menuContribution
+ allPopups="false"
+ locationURI="popup:org.eclipse.ui.popup.any?after=additions">
+ <menu
+ id="org.eclipse.papyrus.infra.types.ui.refactor"
+ label="%menu.refactor.label">
+ <command
+ commandId="org.eclipse.papyrus.infra.types.ui.commands.negateRule"
+ label="%command.negate.label"
+ style="push">
+ </command>
+ <command
+ commandId="org.eclipse.papyrus.infra.types.ui.commands.addToAndRule"
+ label="%command.addToAnd.label"
+ style="push">
+ </command>
+ <command
+ commandId="org.eclipse.papyrus.infra.types.ui.commands.addToOrRule"
+ label="%command.addToOr.label"
+ style="push">
+ </command>
+ <command
+ commandId="org.eclipse.papyrus.infra.types.ui.commands.pullUpOperandRule"
+ label="%command.pullUp.label"
+ style="push">
+ </command>
+ <visibleWhen
+ checkEnabled="false">
+ <with
+ variable="selection">
+ <and>
+ <count
+ value="1">
+ </count>
+ <iterate
+ ifEmpty="false"
+ operator="or">
+ <adapt
+ type="org.eclipse.emf.ecore.EObject">
+ <instanceof
+ value="org.eclipse.papyrus.infra.types.rulebased.RuleConfiguration">
+ </instanceof>
+ </adapt>
+ </iterate>
+ </and>
+ </with>
+ </visibleWhen>
+ </menu>
+ </menuContribution>
+ </extension>
</plugin>
diff --git a/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/RuleRefactoringHandler.java b/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/RuleRefactoringHandler.java
new file mode 100644
index 00000000000..15ccdfd56ec
--- /dev/null
+++ b/plugins/infra/types/org.eclipse.papyrus.infra.types.ui/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/RuleRefactoringHandler.java
@@ -0,0 +1,209 @@
+/*****************************************************************************
+ * Copyright (c) 2021 Christian W. Damus, CEA LIST, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.types.core.internal.ui.handlers;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.BiFunction;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExecutableExtension;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.command.CommandWrapper;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.edit.command.AddCommand;
+import org.eclipse.emf.edit.command.RemoveCommand;
+import org.eclipse.emf.edit.command.SetCommand;
+import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
+import org.eclipse.papyrus.infra.types.rulebased.Activator;
+import org.eclipse.papyrus.infra.types.rulebased.AndRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.CompositeRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.NotRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.OrRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.RuleBasedFactory;
+import org.eclipse.papyrus.infra.types.rulebased.RuleBasedPackage;
+import org.eclipse.papyrus.infra.types.rulebased.RuleConfiguration;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.handlers.HandlerUtil;
+
+/**
+ * Handler for the rule refactoring commands.
+ */
+public class RuleRefactoringHandler extends AbstractHandler implements IExecutableExtension {
+
+ /** The operation initialization parameter in the <tt>plugin.xml</tt>. */
+ private static final String PARAM_OPERATION = "operation"; //$NON-NLS-1$
+
+ private BiFunction<EditingDomain, RuleConfiguration, Command> refactoringOperation;
+
+ public RuleRefactoringHandler() {
+ super();
+ }
+
+ @Override
+ public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
+ if (data instanceof Map) {
+ Map<?, ?> parameters = (Map<?, ?>) data;
+ String operationName = (String) parameters.get(PARAM_OPERATION);
+ if (operationName == null || operationName.isBlank()) {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ "Missing operation parameter in RuleRefactoringHandler initialization")); //$NON-NLS-1$
+ } else {
+ switch (OperationKind.valueOf(String.valueOf(operationName))) {
+ case NEGATE:
+ refactoringOperation = this::getNegateCommand;
+ break;
+ case ADD_TO_AND:
+ refactoringOperation = this::getAddToAndCommand;
+ break;
+ case ADD_TO_OR:
+ refactoringOperation = this::getAddToOrCommand;
+ break;
+ case PULL_UP_OPERAND:
+ refactoringOperation = this::getPullUpOperandCommand;
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ RuleConfiguration rule = getRuleConfiguration(event.getApplicationContext());
+
+ if (rule != null) {
+ try {
+ execute(rule, event.getCommand().getName());
+ } catch (NotDefinedException e) {
+ throw new ExecutionException("Cannot get command label", e); //$NON-NLS-1$
+ }
+ }
+
+ return null;
+ }
+
+ protected void execute(RuleConfiguration rule, String label) {
+ EditingDomain domain = AdapterFactoryEditingDomain.getEditingDomainFor(rule);
+ domain.getCommandStack().execute(new CommandWrapper(label, label, null) {
+ @Override
+ protected Command createCommand() {
+ return refactoringOperation.apply(domain, rule);
+ }
+ });
+ }
+
+ private RuleConfiguration getRuleConfiguration(Object evaluationContext) {
+ RuleConfiguration result = null;
+
+ Object selection = HandlerUtil.getVariable(evaluationContext, ISources.ACTIVE_CURRENT_SELECTION_NAME);
+ if (selection instanceof Iterable<?>) {
+ for (Iterator<?> iter = ((Iterable<?>) selection).iterator(); result == null && iter.hasNext();) {
+ EObject next = EMFHelper.getEObject(iter.next());
+ if (next instanceof RuleConfiguration) {
+ result = (RuleConfiguration) next;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ RuleConfiguration getContainingRule(EditingDomain domain, RuleConfiguration rule) {
+ EObject container = rule.eContainer();
+
+ if (container == null) {
+ // The rule may be referenced by a NotRuleConfiguration, which is its logical 'container'
+ // despite not actually being a container (cf. bug 571560)
+ container = EcoreUtil.UsageCrossReferencer.find(rule, domain.getResourceSet()).stream()
+ .filter(setting -> setting.getEStructuralFeature() == RuleBasedPackage.Literals.NOT_RULE_CONFIGURATION__COMPOSED_RULE)
+ .map(EStructuralFeature.Setting::getEObject)
+ .findFirst()
+ .orElse(null);
+ }
+
+ return (container instanceof RuleConfiguration) ? (RuleConfiguration) container : null;
+ }
+
+ protected Command replace(EditingDomain domain, EObject object, EObject replacement) {
+ EObject container = object.eContainer();
+ EReference containment = object.eContainmentFeature();
+ return containment.isMany()
+ ? RemoveCommand.create(domain, container, containment, Set.of(object))
+ .chain(AddCommand.create(domain, container, containment, Set.of(replacement)))
+ : SetCommand.create(domain, object.eContainer(), containment, replacement);
+ }
+
+ private Command getNegateCommand(EditingDomain domain, RuleConfiguration rule) {
+ RuleConfiguration parent = getContainingRule(domain, rule);
+ if (parent instanceof NotRuleConfiguration) {
+ // Negate the parent, instead (recursively)
+ return getNegateCommand(domain, parent);
+ } else if (rule instanceof NotRuleConfiguration) {
+ NotRuleConfiguration notRule = (NotRuleConfiguration) rule;
+ return SetCommand.create(domain, notRule, RuleBasedPackage.Literals.NOT_RULE_CONFIGURATION__COMPOSED_RULE, SetCommand.UNSET_VALUE)
+ .chain(replace(domain, notRule, notRule.getComposedRule()));
+ } else {
+ NotRuleConfiguration notRule = RuleBasedFactory.eINSTANCE.createNotRuleConfiguration();
+
+ return replace(domain, rule, notRule)
+ .chain(SetCommand.create(domain, notRule, RuleBasedPackage.Literals.NOT_RULE_CONFIGURATION__COMPOSED_RULE, rule));
+ }
+ }
+
+ private Command getAddToAndCommand(EditingDomain domain, RuleConfiguration rule) {
+ AndRuleConfiguration andRule = RuleBasedFactory.eINSTANCE.createAndRuleConfiguration();
+ return replace(domain, rule, andRule)
+ .chain(AddCommand.create(domain, andRule, RuleBasedPackage.Literals.COMPOSITE_RULE_CONFIGURATION__COMPOSED_RULES, Set.of(rule)));
+ }
+
+ private Command getAddToOrCommand(EditingDomain domain, RuleConfiguration rule) {
+ OrRuleConfiguration orRule = RuleBasedFactory.eINSTANCE.createOrRuleConfiguration();
+ return replace(domain, rule, orRule)
+ .chain(AddCommand.create(domain, orRule, RuleBasedPackage.Literals.COMPOSITE_RULE_CONFIGURATION__COMPOSED_RULES, Set.of(rule)));
+ }
+
+ private Command getPullUpOperandCommand(EditingDomain domain, RuleConfiguration rule) {
+ CompositeRuleConfiguration composite = (CompositeRuleConfiguration) rule;
+ RuleConfiguration operand = composite.getComposedRules().get(0);
+ return RemoveCommand.create(domain, composite, RuleBasedPackage.Literals.COMPOSITE_RULE_CONFIGURATION__COMPOSED_RULES, Set.of(operand))
+ .chain(replace(domain, composite, operand));
+ }
+
+ //
+ // Nested types
+ //
+
+ /**
+ * Enumeration of refactoring operations supported.
+ */
+ public static enum OperationKind {
+ NEGATE, ADD_TO_AND, ADD_TO_OR, PULL_UP_OPERAND;
+ }
+
+}
diff --git a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java
index d8a113e2ca7..74f055ca913 100644
--- a/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java
+++ b/tests/junit/framework/org.eclipse.papyrus.junit.utils/src/org/eclipse/papyrus/junit/matchers/MoreMatchers.java
@@ -1,5 +1,5 @@
/*****************************************************************************
- * Copyright (c) 2014, 2020 Christian W. Damus, CEA LIST, and others.
+ * Copyright (c) 2014, 2021 Christian W. Damus, CEA LIST, and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -19,6 +19,8 @@ import java.util.regex.Pattern;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.util.EcoreUtil;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.FeatureMatcher;
@@ -334,4 +336,18 @@ public class MoreMatchers {
return hasCount(greaterThanOrEqual(min), elementMatcher);
}
+ public static Matcher<EObject> eEqualTo(EObject eObject) {
+ return new TypeSafeMatcher<>(EObject.class) {
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("is structurally equal to ").appendValue(eObject);
+ }
+
+ @Override
+ protected boolean matchesSafely(EObject item) {
+ return EcoreUtil.equals(item, eObject);
+ }
+ };
+ }
+
}
diff --git a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/.classpath b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/.classpath
index e801ebfb468..e54abfc9d66 100644
--- a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/.classpath
+++ b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/.classpath
@@ -1,7 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
- <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
+ <attributes>
+ <attribute name="module" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins">
+ <accessrules>
+ <accessrule kind="accessible" pattern="org/eclipse/papyrus/infra/types/core/internal/ui/handlers/**"/>
+ </accessrules>
+ </classpathentry>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/META-INF/MANIFEST.MF b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/META-INF/MANIFEST.MF
index 7fca087d880..a1e4f625c94 100644
--- a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/META-INF/MANIFEST.MF
+++ b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/META-INF/MANIFEST.MF
@@ -7,7 +7,13 @@ Require-Bundle: org.eclipse.gmf.runtime.diagram.ui;bundle-version="[1.9.0,2.0.0)
org.eclipse.papyrus.junit.framework;bundle-version="[2.0.0,3.0.0)",
org.eclipse.papyrus.junit.utils;bundle-version="[3.0.0,4.0.0)",
org.junit;bundle-version="[4.13.0,5.0.0)",
- org.eclipse.papyrus.infra.newchild;bundle-version="5.0.0",
+ org.eclipse.papyrus.infra.newchild;bundle-version="[5.0.0,6.0.0)",
+ org.eclipse.papyrus.infra.types;bundle-version="[5.0.0,6.0.0)",
+ org.eclipse.papyrus.infra.types.rulebased;bundle-version="[5.0.0,6.0.0)",
+ org.eclipse.papyrus.infra.types.ui;bundle-version="[4.0.0,5.0.0)",
+ org.eclipse.core.expressions;bundle-version="[3.7.100,4.0.0)",
+ org.eclipse.emf.common.ui;bundle-version="[2.18.0,3.0.0)",
+ org.eclipse.papyrus.infra.types.editor;bundle-version="[4.0.0,5.0.0)",
org.eclipse.uml2.uml;bundle-version="[5.5.0,6.0.0)"
Export-Package: org.eclipse.papyrus.infra.types.ui.tests
Bundle-Vendor: %providerName
@@ -19,3 +25,4 @@ Bundle-ManifestVersion: 2
Bundle-SymbolicName: org.eclipse.papyrus.infra.types.ui.tests;singleton:=true
Bundle-RequiredExecutionEnvironment: JavaSE-11
Automatic-Module-Name: org.eclipse.papyrus.infra.types.ui.tests
+Import-Package: com.google.common.base;version="[30.1.0,31.0.0)"
diff --git a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/org.eclipse.papyrus.infra.types.ui.tests.launch b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/org.eclipse.papyrus.infra.types.ui.tests.launch
index 2a532c13af5..414b4ddd276 100644
--- a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/org.eclipse.papyrus.infra.types.ui.tests.launch
+++ b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/org.eclipse.papyrus.infra.types.ui.tests.launch
@@ -1,42 +1,43 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.pde.ui.JunitLaunchConfig">
-<booleanAttribute key="append.args" value="true"/>
-<booleanAttribute key="askclear" value="false"/>
-<booleanAttribute key="automaticAdd" value="true"/>
-<booleanAttribute key="automaticValidate" value="false"/>
-<stringAttribute key="bootstrap" value=""/>
-<stringAttribute key="checked" value="[NONE]"/>
-<booleanAttribute key="clearConfig" value="true"/>
-<booleanAttribute key="clearws" value="true"/>
-<booleanAttribute key="clearwslog" value="false"/>
-<stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/pde-junit"/>
-<booleanAttribute key="default" value="true"/>
-<booleanAttribute key="includeOptional" value="true"/>
-<stringAttribute key="location" value="${workspace_loc}/../junit-workspacemd5"/>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
-<listEntry value="/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/ui/tests/AllTests.java"/>
-</listAttribute>
-<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
-<listEntry value="1"/>
-</listAttribute>
-<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
-<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
-<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
-<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
-<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
-<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
-<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.papyrus.infra.types.ui.tests.AllTests"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog"/>
-<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.papyrus.infra.types.ui.tests"/>
-<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
-<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx1024m "/>
-<stringAttribute key="pde.version" value="3.3"/>
-<stringAttribute key="product" value="org.eclipse.platform.ide"/>
-<booleanAttribute key="run_in_ui_thread" value="false"/>
-<booleanAttribute key="show_selected_only" value="false"/>
-<booleanAttribute key="tracing" value="false"/>
-<booleanAttribute key="useCustomFeatures" value="false"/>
-<booleanAttribute key="useDefaultConfig" value="true"/>
-<booleanAttribute key="useDefaultConfigArea" value="false"/>
-<booleanAttribute key="useProduct" value="true"/>
+ <booleanAttribute key="append.args" value="true"/>
+ <booleanAttribute key="askclear" value="false"/>
+ <booleanAttribute key="automaticAdd" value="true"/>
+ <booleanAttribute key="automaticValidate" value="false"/>
+ <stringAttribute key="bootstrap" value=""/>
+ <stringAttribute key="checked" value="[NONE]"/>
+ <booleanAttribute key="clearConfig" value="true"/>
+ <booleanAttribute key="clearws" value="true"/>
+ <booleanAttribute key="clearwslog" value="false"/>
+ <stringAttribute key="configLocation" value="${workspace_loc}/.metadata/.plugins/org.eclipse.pde.core/pde-junit"/>
+ <booleanAttribute key="default" value="true"/>
+ <booleanAttribute key="includeOptional" value="true"/>
+ <stringAttribute key="location" value="${workspace_loc}/../junit-workspacemd5"/>
+ <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+ <listEntry value="/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/ui/tests/AllTests.java"/>
+ </listAttribute>
+ <listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+ <listEntry value="1"/>
+ </listAttribute>
+ <stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value=""/>
+ <booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+ <stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+ <stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_ATTR_USE_ARGFILE" value="false"/>
+ <booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
+ <stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"/>
+ <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.papyrus.infra.types.ui.tests.AllTests"/>
+ <stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog"/>
+ <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="org.eclipse.papyrus.infra.types.ui.tests"/>
+ <stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
+ <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms40m -Xmx1024m"/>
+ <stringAttribute key="pde.version" value="3.3"/>
+ <stringAttribute key="product" value="org.eclipse.platform.ide"/>
+ <booleanAttribute key="run_in_ui_thread" value="true"/>
+ <booleanAttribute key="show_selected_only" value="false"/>
+ <booleanAttribute key="tracing" value="false"/>
+ <booleanAttribute key="useCustomFeatures" value="false"/>
+ <booleanAttribute key="useDefaultConfig" value="true"/>
+ <booleanAttribute key="useDefaultConfigArea" value="false"/>
+ <booleanAttribute key="useProduct" value="true"/>
</launchConfiguration>
diff --git a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/tests/RuleRefactoringHandlerTest.java b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/tests/RuleRefactoringHandlerTest.java
new file mode 100644
index 00000000000..2e31f944d8f
--- /dev/null
+++ b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/tests/RuleRefactoringHandlerTest.java
@@ -0,0 +1,396 @@
+/*****************************************************************************
+ * Copyright (c) 2021 Christian W. Damus, CEA LIST, and others.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Christian W. Damus - Initial API and implementation
+ *
+ *****************************************************************************/
+
+package org.eclipse.papyrus.infra.types.core.internal.ui.handlers.tests;
+
+import static org.eclipse.papyrus.junit.matchers.MoreMatchers.eEqualTo;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.IOException;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.net.URL;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import org.eclipse.core.commands.Command;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.NotEnabledException;
+import org.eclipse.core.commands.NotHandledException;
+import org.eclipse.core.commands.common.NotDefinedException;
+import org.eclipse.core.expressions.EvaluationContext;
+import org.eclipse.core.expressions.IEvaluationContext;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.emf.common.ui.URIEditorInput;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.emf.edit.domain.IEditingDomainProvider;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.papyrus.infra.types.ElementTypeSetConfiguration;
+import org.eclipse.papyrus.infra.types.core.internal.ui.handlers.RuleRefactoringHandler.OperationKind;
+import org.eclipse.papyrus.infra.types.rulebased.AndRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.CompositeRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.NotRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.OrRuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.RuleBasedFactory;
+import org.eclipse.papyrus.infra.types.rulebased.RuleBasedPackage;
+import org.eclipse.papyrus.infra.types.rulebased.RuleBasedTypeConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.RuleConfiguration;
+import org.eclipse.papyrus.infra.types.rulebased.util.RuleBasedSwitch;
+import org.eclipse.papyrus.junit.utils.rules.AnnotationRule;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.ISources;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+/**
+ * Test cases for the rule refactoring handlers.
+ */
+public class RuleRefactoringHandlerTest {
+
+ @Rule
+ public final AnnotationRule<OperationKind> refactoringKind = AnnotationRule.create(Refactoring.class, null);
+
+ private String commandID;
+
+ private IEditorPart editor;
+ private EditingDomain domain;
+ private ElementTypeSetConfiguration fixture;
+ private ElementTypeSetConfiguration originalState;
+ private ElementTypeSetConfiguration newState;
+ private RuleBasedTypeConfiguration type;
+ private RuleConfiguration rule;
+ private boolean skipUndoRedo;
+ private ECrossReferenceAdapter xrefs;
+
+ public RuleRefactoringHandlerTest() {
+ super();
+ }
+
+ @Refactoring(OperationKind.NEGATE)
+ @Test
+ public void negate() {
+ execute(rule);
+
+ NotRuleConfiguration notRule = getNegatingRule(rule);
+ assertThat("Rule not negated", notRule, notNullValue());
+
+ if (RuleBasedPackage.Literals.NOT_RULE_CONFIGURATION__COMPOSED_RULE.isContainment()) {
+ assertThat("Rule lost in space", rule.eContainer().eContainer(), sameInstance(type));
+ }
+ }
+
+ @Refactoring(OperationKind.NEGATE)
+ @Test
+ public void negate_notRule() {
+ NotRuleConfiguration notRule = wrapInNot();
+
+ execute(notRule);
+
+ assertThat("Rule not negated", rule.eContainer(), sameInstance(type));
+ }
+
+ @Refactoring(OperationKind.NEGATE)
+ @Test
+ public void negate_negatedRule() {
+ NotRuleConfiguration notRule = wrapInNot();
+
+ if (!RuleBasedPackage.Literals.NOT_RULE_CONFIGURATION__COMPOSED_RULE.isContainment()) {
+ // The not rule detached the object, so put it in the resource for the handler to find
+ // the editing domain context. cf. bug 571560
+ notRule.eResource().getContents().add(rule);
+ }
+
+ execute(rule);
+
+ assertThat("Rule not negated", rule.eContainer(), sameInstance(type));
+ }
+
+ @Refactoring(OperationKind.ADD_TO_AND)
+ @Test
+ public void addToAnd() {
+ execute(rule);
+
+ assertThat("Rule not wrapped in 'and'", rule.eContainer(), instanceOf(AndRuleConfiguration.class));
+ assertThat("Rule lost in space", rule.eContainer().eContainer(), sameInstance(type));
+ }
+
+ @Refactoring(OperationKind.ADD_TO_AND)
+ @Test
+ public void alreadyInAnd() {
+ wrapInAnd();
+
+ assertDisabled(rule);
+ }
+
+ @Refactoring(OperationKind.ADD_TO_OR)
+ @Test
+ public void addToOr() {
+ execute(rule);
+
+ assertThat("Rule not wrapped in 'or'", rule.eContainer(), instanceOf(OrRuleConfiguration.class));
+ assertThat("Rule lost in space", rule.eContainer().eContainer(), sameInstance(type));
+ }
+
+ @Refactoring(OperationKind.ADD_TO_OR)
+ @Test
+ public void alreadyInOr() {
+ wrapInOr();
+
+ assertDisabled(rule);
+ }
+
+ @Refactoring(OperationKind.PULL_UP_OPERAND)
+ @Test
+ public void pullUpAnd() {
+ AndRuleConfiguration andRule = wrapInAnd();
+
+ execute(andRule);
+
+ assertThat("And rule not elided", rule.eContainer(), sameInstance(type));
+ }
+
+ @Refactoring(OperationKind.PULL_UP_OPERAND)
+ @Test
+ public void pullUpAndMultiple() {
+ AndRuleConfiguration andRule = wrapInAnd();
+ andRule.getComposedRules().add(RuleBasedFactory.eINSTANCE.createNotRuleConfiguration());
+
+ assertDisabled(andRule);
+ }
+
+ @Refactoring(OperationKind.PULL_UP_OPERAND)
+ @Test
+ public void pullUpOr() {
+ OrRuleConfiguration orRule = wrapInOr();
+
+ execute(orRule);
+
+ assertThat("Or", rule.eContainer(), sameInstance(type));
+ }
+
+ @Refactoring(OperationKind.PULL_UP_OPERAND)
+ @Test
+ public void pullUpOrMultiple() {
+ OrRuleConfiguration orRule = wrapInOr();
+ orRule.getComposedRules().add(RuleBasedFactory.eINSTANCE.createNotRuleConfiguration());
+
+ assertDisabled(orRule);
+ }
+
+ @Refactoring(OperationKind.PULL_UP_OPERAND)
+ @Test
+ public void pullUpNot() {
+ NotRuleConfiguration notRule = wrapInNot();
+
+ assertDisabled(notRule);
+ }
+
+ //
+ // Test framework
+ //
+
+ @Before
+ public void createHandler() throws CoreException {
+ switch (refactoringKind.get()) {
+ case NEGATE:
+ commandID = "org.eclipse.papyrus.infra.types.ui.commands.negateRule"; //$NON-NLS-1$
+ break;
+ case ADD_TO_AND:
+ commandID = "org.eclipse.papyrus.infra.types.ui.commands.addToAndRule"; //$NON-NLS-1$
+ break;
+ case ADD_TO_OR:
+ commandID = "org.eclipse.papyrus.infra.types.ui.commands.addToOrRule"; //$NON-NLS-1$
+ break;
+ case PULL_UP_OPERAND:
+ commandID = "org.eclipse.papyrus.infra.types.ui.commands.pullUpOperandRule"; //$NON-NLS-1$
+ break;
+ }
+ }
+
+ @Before
+ public void loadFixture() throws PartInitException, IOException {
+ // Ensure a file: URI so that the editing domain doesn't think it's read-only,
+ // which would then result in no commands being executable
+ URL url = FileLocator.toFileURL(RuleRefactoringHandlerTest.class.getResource("test.elementtypesconfigurations"));
+ URI uri = URI.createURI(url.toExternalForm(), true);
+
+ IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+ if (window == null) {
+ window = PlatformUI.getWorkbench().getWorkbenchWindows()[0];
+ }
+
+ editor = window.getActivePage().openEditor(new URIEditorInput(uri), "org.eclipse.papyrus.infra.types.presentation.ElementTypesConfigurationsEditorID");
+ domain = ((IEditingDomainProvider) editor).getEditingDomain();
+
+ Resource resource = domain.getResourceSet().getResource(uri, true);
+ fixture = (ElementTypeSetConfiguration) resource.getContents().get(0);
+ type = (RuleBasedTypeConfiguration) fixture.getElementTypeConfigurations().get(0);
+ rule = type.getRuleConfiguration();
+
+ originalState = EcoreUtil.copy(fixture);
+
+ xrefs = new ECrossReferenceAdapter();
+ domain.getResourceSet().eAdapters().add(xrefs);
+ }
+
+ @After
+ public void undoAndRedo() {
+ try {
+ if (!skipUndoRedo) {
+ undo();
+ redo();
+ }
+ } finally {
+ if (editor != null) {
+ editor.getEditorSite().getPage().closeEditor(editor, false);
+ }
+ }
+ }
+
+ <T extends RuleConfiguration> T updateInitialState(BiFunction<? super RuleBasedTypeConfiguration, ? super RuleConfiguration, T> update) {
+ T result = update.apply(type, rule);
+ originalState = EcoreUtil.copy(fixture);
+ return result;
+ }
+
+ NotRuleConfiguration wrapInNot() {
+ return compose(RuleBasedPackage.Literals.NOT_RULE_CONFIGURATION);
+ }
+
+ AndRuleConfiguration wrapInAnd() {
+ return compose(RuleBasedPackage.Literals.AND_RULE_CONFIGURATION);
+ }
+
+ OrRuleConfiguration wrapInOr() {
+ return compose(RuleBasedPackage.Literals.OR_RULE_CONFIGURATION);
+ }
+
+ <T extends RuleConfiguration> T compose(EClass compositeType) {
+ @SuppressWarnings("unchecked")
+ T result = (T) updateInitialState((type, rule) -> {
+ return new RuleBasedSwitch<RuleConfiguration>() {
+ @Override
+ public RuleConfiguration caseNotRuleConfiguration(NotRuleConfiguration object) {
+ object.setComposedRule(rule);
+ type.setRuleConfiguration(object);
+ return object;
+ }
+
+ @Override
+ public RuleConfiguration caseCompositeRuleConfiguration(CompositeRuleConfiguration object) {
+ object.getComposedRules().add(rule);
+ type.setRuleConfiguration(object);
+ return object;
+ }
+ }.doSwitch(RuleBasedFactory.eINSTANCE.create(compositeType));
+ });
+ return result;
+ }
+
+ void execute(RuleConfiguration rule) {
+ // In case execution fails, there will be no point in testing undo/redo after the fact
+ skipUndoRedo = true;
+
+ IStructuredSelection selection = new StructuredSelection(rule);
+ ((ISelectionProvider) editor).setSelection(selection);
+
+ ICommandService commandService = editor.getSite().getService(ICommandService.class);
+ Command command = commandService.getCommand(commandID);
+
+ IEvaluationContext context = new EvaluationContext(null, selection);
+ context.addVariable(ISources.ACTIVE_CURRENT_SELECTION_NAME, selection);
+ ExecutionEvent event = new ExecutionEvent(command, Map.of(), this, context);
+
+ assertThat("Cannot execute refactoring handler", command.isEnabled(), is(true));
+ try {
+ command.executeWithChecks(event);
+ } catch (NotDefinedException | NotEnabledException | NotHandledException | ExecutionException e) {
+ throw new AssertionError("Failed to execute refactoring handler", e);
+ }
+
+ // This test will perform the undo/redo assertion after the fact
+ skipUndoRedo = false;
+
+ newState = EcoreUtil.copy(fixture);
+ }
+
+ void assertDisabled(RuleConfiguration rule) {
+ // This test will skip the undo/redo assertion after the fact
+ skipUndoRedo = true;
+
+ IStructuredSelection selection = new StructuredSelection(rule);
+ ((ISelectionProvider) editor).setSelection(selection);
+
+ ICommandService commandService = editor.getSite().getService(ICommandService.class);
+ Command command = commandService.getCommand(commandID);
+
+ assertThat("Can execute refactoring handler", command.isEnabled(), is(false));
+ }
+
+ void undo() {
+ assertThat("Cannot undo refactoring command", domain.getCommandStack().canUndo(), is(true));
+ domain.getCommandStack().undo();
+
+ assertThat("Undo did not revert to original state", fixture, eEqualTo(originalState));
+ }
+
+ void redo() {
+ assertThat("Cannot redo refactoring command", domain.getCommandStack().canRedo(), is(true));
+ domain.getCommandStack().redo();
+
+ assertThat("Redo did not restore to new state", fixture, eEqualTo(newState));
+ }
+
+ NotRuleConfiguration getNegatingRule(RuleConfiguration rule) {
+ return xrefs.getInverseReferences(rule, RuleBasedPackage.Literals.NOT_RULE_CONFIGURATION__COMPOSED_RULE, true).stream()
+ .map(EStructuralFeature.Setting::getEObject)
+ .map(NotRuleConfiguration.class::cast)
+ .findFirst()
+ .orElse(null);
+ }
+
+ //
+ // Nested types
+ //
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.METHOD)
+ public @interface Refactoring {
+ OperationKind value();
+ }
+
+}
diff --git a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/tests/test.elementtypesconfigurations b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/tests/test.elementtypesconfigurations
new file mode 100644
index 00000000000..8aea208a39a
--- /dev/null
+++ b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/core/internal/ui/handlers/tests/test.elementtypesconfigurations
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<elementtypesconfigurations:ElementTypeSetConfiguration xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:elementtypesconfigurations="http://www.eclipse.org/papyrus/infra/elementtypesconfigurations/1.2" xmlns:invariantcontainerrule="http://www.eclipse.org/papyrus/emf/types/invariantcontainerrule/1.1" xmlns:rulebased="http://www.eclipse.org/papyrus/infra/types/rulebased/1.1" xmi:id="_vsk9QHkMEeutlP5NaIjAJg" identifier="org.eclipse.papyrus.infra.types.ui.tests.Set" name="Test Fixture" metamodelNsURI="http://www.eclipse.org/uml2/5.0.0/UML">
+ <elementTypeConfigurations xmi:type="rulebased:RuleBasedTypeConfiguration" xmi:id="_2TOhIHkMEeutlP5NaIjAJg" identifier="org.eclipse.papyrus.infra.types.ui.tests.type" name="Test Type" hint="" kind="org.eclipse.gmf.runtime.emf.type.core.IHintedType">
+ <ruleConfiguration xmi:type="invariantcontainerrule:InvariantContainerRuleConfiguration" xmi:id="_61_hsHkMEeutlP5NaIjAJg">
+ <permissions xmi:type="invariantcontainerrule:HierarchyPermission" xmi:id="_72XZkHkMEeutlP5NaIjAJg" containerType="uml.Package" permitted="true"/>
+ </ruleConfiguration>
+ </elementTypeConfigurations>
+</elementtypesconfigurations:ElementTypeSetConfiguration>
diff --git a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/ui/tests/AllTests.java b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/ui/tests/AllTests.java
index 560333c92ba..28ab15104b2 100644
--- a/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/ui/tests/AllTests.java
+++ b/tests/junit/plugins/infra/types/org.eclipse.papyrus.infra.types.ui.tests/src/org/eclipse/papyrus/infra/types/ui/tests/AllTests.java
@@ -1,6 +1,6 @@
/*****************************************************************************
- * Copyright (c) 2010, 2016 CEA LIST, Christian W. Damus, and others.
- *
+ * Copyright (c) 2010, 2021 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 2.0
* which accompanies this distribution, and is available at
@@ -10,11 +10,12 @@
*
* Contributors:
* Francois Le Fevre (CEA LIST) francois.le-fevre@cea.fr - Initial API and implementation
- * Christian W. Damus - bug 485220
- *
+ * Christian W. Damus - bugs 485220, 571561
+ *
*****************************************************************************/
package org.eclipse.papyrus.infra.types.ui.tests;
+import org.eclipse.papyrus.infra.types.core.internal.ui.handlers.tests.RuleRefactoringHandlerTest;
import org.eclipse.papyrus.infra.types.tests.ElementTypesRegistryTests;
import org.eclipse.papyrus.junit.framework.classification.ClassificationSuite;
import org.junit.runner.RunWith;
@@ -25,7 +26,10 @@ import org.junit.runners.Suite.SuiteClasses;
* All tests for this fragment
*/
@RunWith(ClassificationSuite.class)
-@SuiteClasses({ ElementTypesRegistryTests.class })
+@SuiteClasses({
+ ElementTypesRegistryTests.class,
+ RuleRefactoringHandlerTest.class,
+})
public class AllTests {
}

Back to the top