Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian W. Damus2013-10-24 13:05:40 +0000
committerChristian W. Damus2013-10-26 00:18:24 +0000
commitfc30e594c7ebc63a773005168b12d5b23f7fbef6 (patch)
treee7063e0e13de2fcaeb2f2921d9fa00e4c2f8489d
parent4990b38219f6728d27180d15ab5b421edeb12d8c (diff)
downloadorg.eclipse.uml2-fc30e594c7ebc63a773005168b12d5b23f7fbef6.tar.gz
org.eclipse.uml2-fc30e594c7ebc63a773005168b12d5b23f7fbef6.tar.xz
org.eclipse.uml2-fc30e594c7ebc63a773005168b12d5b23f7fbef6.zip
[180744] template expansion support
https://bugs.eclipse.org/bugs/show_bug.cgi?id=180744 Implement a TemplateExpander utility and "Expand Templates..." action in the editor menu. Based on a backwards-compatible refactoring of the PackageMerger. Includes JUnit tests. Recursive (nested) template expansion and specialization of redefinable templates are not supported.
-rw-r--r--plugins/org.eclipse.uml2.uml.editor/plugin.properties5
-rw-r--r--plugins/org.eclipse.uml2.uml.editor/plugin.xml16
-rw-r--r--plugins/org.eclipse.uml2.uml.editor/src/org/eclipse/uml2/uml/editor/actions/ExpandTemplatesAction.java163
-rw-r--r--plugins/org.eclipse.uml2.uml.editor/src/org/eclipse/uml2/uml/editor/dialogs/TemplateExpanderOptionsDialog.java49
-rw-r--r--plugins/org.eclipse.uml2.uml/plugin.properties6
-rw-r--r--plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java888
-rw-r--r--tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744.profile.uml24
-rw-r--r--tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744.uml200
-rw-r--r--tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744Test.java344
-rw-r--r--tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/UMLBugTests.java3
10 files changed, 1617 insertions, 81 deletions
diff --git a/plugins/org.eclipse.uml2.uml.editor/plugin.properties b/plugins/org.eclipse.uml2.uml.editor/plugin.properties
index 2b9b038af..026988a98 100644
--- a/plugins/org.eclipse.uml2.uml.editor/plugin.properties
+++ b/plugins/org.eclipse.uml2.uml.editor/plugin.properties
@@ -8,7 +8,7 @@
# IBM - initial API and implementation
# Kenn Hussey (Embarcadero Technologies) - 227392, 226178, 226101, 204200
# Kenn Hussey (CEA) - 327039
-# Christian W. Damus (CEA) - 326915, 268444
+# Christian W. Damus (CEA) - 326915, 268444, 180744
#
# NLS_MESSAGEFORMAT_VAR
@@ -73,6 +73,7 @@ _UI_StereotypeMenu_label = &Stereotype
_UI_ApplyStereotypeAction_label = &Apply Stereotype...
_UI_DestroyElementAction_label = &Destroy
_UI_UnapplyStereotypeAction_label = &Unapply Stereotype...
+_UI_ExpandTemplatesAction_label = Expand &Templates...
_UI_ApplyProfileAction_label = &Apply Profile...
_UI_ImportPrimitiveTypeAction_label = &Import Primitive Type...
@@ -88,6 +89,7 @@ _UI_CreateExtensionAction_label = &Create Extension...
_UI_ApplyStereotypeActionCommand_label = Apply Stereotype(s)
_UI_DestroyElementActionCommand_label = Destroy ''{0}''
_UI_UnapplyStereotypeActionCommand_label = Unapply Stereotype(s)
+_UI_ExpandTemplatesActionCommand_label = Expand Template(s) of ''{0}''
_UI_ApplyProfileActionCommand_label = Apply Profile(s)
_UI_ImportPrimitiveTypeActionCommand_label = Import Primitive Type(s)
@@ -101,6 +103,7 @@ _UI_ReferenceMetamodelActionCommand_label = Reference Metamodel(s)
_UI_CreateExtensionActionCommand_label = Create Extension(s)
_UI_MergingPackage_message = Merging package ''{0}''...
+_UI_ExpandingTemplates_message = Expanding templates of ''{0}''...
_UI_DiagnosticOK_message = The operation was successful.
_UI_DiagnosticProblems_message = Problems were encountered.
diff --git a/plugins/org.eclipse.uml2.uml.editor/plugin.xml b/plugins/org.eclipse.uml2.uml.editor/plugin.xml
index 4f3ba6a43..cdc6b7d1b 100644
--- a/plugins/org.eclipse.uml2.uml.editor/plugin.xml
+++ b/plugins/org.eclipse.uml2.uml.editor/plugin.xml
@@ -2,7 +2,7 @@
<?eclipse version="3.0"?>
<!--
- Copyright (c) 2005, 2008 IBM Corporation, Embarcadero Technologies, and others.
+ Copyright (c) 2005, 2008 IBM Corporation, Embarcadero Technologies, CEA, 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
@@ -11,8 +11,8 @@
Contributors:
IBM - initial API and implementation
Kenn Hussey (Embarcadero Technologies) - 156879, 204200
+ Christian W. Damus (CEA) - 180744
- $Id: plugin.xml,v 1.13 2008/04/21 13:27:42 khussey Exp $
-->
<plugin>
@@ -176,6 +176,18 @@
<separator name="additions"/>
</menu>
<action
+ label="%_UI_ExpandTemplatesAction_label"
+ class="org.eclipse.uml2.uml.editor.actions.ExpandTemplatesAction"
+ menubarPath="org.eclipse.uml2.umlMenuID/org.eclipse.uml2.uml.editor.ElementMenuID/additions"
+ enablesFor="1"
+ id="org.eclipse.uml2.uml.editor.ExpandTemplatesActionID">
+ <enablement>
+ <objectClass
+ name="org.eclipse.uml2.uml.TemplateableElement">
+ </objectClass>
+ </enablement>
+ </action>
+ <action
label="%_UI_UnapplyStereotypeAction_label"
class="org.eclipse.uml2.uml.editor.actions.UnapplyStereotypeAction"
menubarPath="org.eclipse.uml2.umlMenuID/org.eclipse.uml2.uml.editor.ElementMenuID/additions"
diff --git a/plugins/org.eclipse.uml2.uml.editor/src/org/eclipse/uml2/uml/editor/actions/ExpandTemplatesAction.java b/plugins/org.eclipse.uml2.uml.editor/src/org/eclipse/uml2/uml/editor/actions/ExpandTemplatesAction.java
new file mode 100644
index 000000000..4880482f4
--- /dev/null
+++ b/plugins/org.eclipse.uml2.uml.editor/src/org/eclipse/uml2/uml/editor/actions/ExpandTemplatesAction.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2005, 2013 IBM Corporation, CEA, 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:
+ * IBM - initial API and implementation
+ * Christian W. Damus (CEA) - Adapted from the MergePackageAction
+ * Christian W. Damus (CEA) - 180744
+ *
+ */
+package org.eclipse.uml2.uml.editor.actions;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.emf.common.command.Command;
+import org.eclipse.emf.common.command.IdentityCommand;
+import org.eclipse.emf.common.command.UnexecutableCommand;
+import org.eclipse.emf.common.util.BasicDiagnostic;
+import org.eclipse.emf.common.util.Diagnostic;
+import org.eclipse.emf.ecore.EValidator;
+import org.eclipse.emf.ecore.plugin.EcorePlugin;
+import org.eclipse.emf.edit.domain.EditingDomain;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.uml2.common.edit.command.ChangeCommand;
+import org.eclipse.uml2.uml.TemplateableElement;
+import org.eclipse.uml2.uml.editor.UMLEditorPlugin;
+import org.eclipse.uml2.uml.editor.dialogs.OptionsDialog;
+import org.eclipse.uml2.uml.editor.dialogs.TemplateExpanderOptionsDialog;
+import org.eclipse.uml2.uml.util.UMLUtil;
+import org.eclipse.uml2.uml.util.UMLValidator;
+
+/**
+ * Menu action that expands the templates in templateable elements that have
+ * template bindings.
+ *
+ * @since 4.2
+ */
+public class ExpandTemplatesAction
+ extends DiagnosticAction {
+
+ public ExpandTemplatesAction() {
+ super();
+ }
+
+ @Override
+ protected Command createActionCommand(EditingDomain editingDomain,
+ Collection<?> collection) {
+
+ if (collection.size() == 1) {
+ Object object = collection.iterator().next();
+
+ if ((object instanceof TemplateableElement)
+ && !((TemplateableElement) object).getTemplateBindings()
+ .isEmpty()) {
+
+ return IdentityCommand.INSTANCE;
+ }
+ }
+
+ return UnexecutableCommand.INSTANCE;
+ }
+
+ @Override
+ public void run(IAction action) {
+
+ if (command != UnexecutableCommand.INSTANCE) {
+ final TemplateableElement templateableElement = (TemplateableElement) collection
+ .iterator().next();
+
+ final Shell shell = PlatformUI.getWorkbench()
+ .getActiveWorkbenchWindow().getShell();
+
+ final Map<String, String> options = new HashMap<String, String>();
+
+ final String label = UMLEditorPlugin.INSTANCE.getString(
+ "_UI_ExpandTemplatesActionCommand_label", //$NON-NLS-1$
+ new Object[]{getLabelProvider().getText(templateableElement)});
+
+ OptionsDialog optionsDialog = new TemplateExpanderOptionsDialog(
+ shell,
+ label,
+ UMLEditorPlugin.INSTANCE.getString("_UI_OptionsDialog_message"), options); //$NON-NLS-1$
+
+ if (optionsDialog.open() == Window.OK) {
+ editingDomain.getCommandStack().execute(
+ new ChangeCommand(editingDomain, new Runnable() {
+
+ public void run() {
+ IRunnableWithProgress runnableWithProgress = new IRunnableWithProgress() {
+
+ public void run(
+ final IProgressMonitor progressMonitor)
+ throws InvocationTargetException,
+ InterruptedException {
+
+ try {
+ final BasicDiagnostic diagnostics = new BasicDiagnostic(
+ UMLValidator.DIAGNOSTIC_SOURCE,
+ 0,
+ EcorePlugin.INSTANCE
+ .getString(
+ "_UI_DiagnosticRoot_diagnostic", //$NON-NLS-1$
+ new Object[]{substitutionLabelProvider
+ .getObjectLabel(templateableElement)}),
+ new Object[]{templateableElement});
+
+ Map<Object, Object> context = new HashMap<Object, Object>();
+ context
+ .put(
+ EValidator.SubstitutionLabelProvider.class,
+ substitutionLabelProvider);
+
+ progressMonitor.beginTask(
+ UMLEditorPlugin.INSTANCE
+ .getString(
+ "_UI_ExpandingTemplates_message", //$NON-NLS-1$
+ new Object[]{substitutionLabelProvider
+ .getObjectLabel(templateableElement)}),
+ IProgressMonitor.UNKNOWN);
+
+ UMLUtil.expand(templateableElement,
+ options, diagnostics, context);
+
+ handleDiagnostic(
+ progressMonitor.isCanceled()
+ ? Diagnostic.CANCEL_INSTANCE
+ : diagnostics, label);
+ } finally {
+ progressMonitor.done();
+ }
+ }
+ };
+
+ if (eclipseResourcesUtil != null) {
+ runnableWithProgress = eclipseResourcesUtil
+ .getWorkspaceModifyOperation(runnableWithProgress);
+ }
+
+ try {
+ new ProgressMonitorDialog(shell).run(false,
+ true, runnableWithProgress);
+ } catch (Exception e) {
+ UMLEditorPlugin.INSTANCE.log(e);
+ }
+ }
+ }, label));
+ }
+ }
+ }
+
+}
diff --git a/plugins/org.eclipse.uml2.uml.editor/src/org/eclipse/uml2/uml/editor/dialogs/TemplateExpanderOptionsDialog.java b/plugins/org.eclipse.uml2.uml.editor/src/org/eclipse/uml2/uml/editor/dialogs/TemplateExpanderOptionsDialog.java
new file mode 100644
index 000000000..c9576c922
--- /dev/null
+++ b/plugins/org.eclipse.uml2.uml.editor/src/org/eclipse/uml2/uml/editor/dialogs/TemplateExpanderOptionsDialog.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013 CEA 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:
+ * CEA - initial API and implementation
+ * Christian W. Damus (CEA) - 180744
+ *
+ */
+package org.eclipse.uml2.uml.editor.dialogs;
+
+import java.util.Map;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.uml2.uml.editor.UMLEditorPlugin;
+import org.eclipse.uml2.uml.util.UMLUtil;
+
+/**
+ * Options dialog for template expansion.
+ *
+ * @since 4.2
+ */
+public class TemplateExpanderOptionsDialog
+ extends OptionsDialog {
+
+ public TemplateExpanderOptionsDialog(Shell parent, String title,
+ String message, Map<String, String> options) {
+
+ super(parent, title, message, options);
+ }
+
+ @Override
+ protected void createOptionAreas(Composite parent) {
+ super.createOptionAreas(parent);
+
+ createOptionArea(
+ parent,
+ UMLEditorPlugin.INSTANCE
+ .getString("_UI_MissingParameterSubstitutions_label"), //$NON-NLS-1$
+ UMLUtil.TemplateExpander.OPTION__MISSING_PARAMETER_SUBSTITUTIONS,
+ new String[]{ignoreChoiceLabel, reportChoiceLabel},
+ reportChoiceLabel);
+ }
+
+}
diff --git a/plugins/org.eclipse.uml2.uml/plugin.properties b/plugins/org.eclipse.uml2.uml/plugin.properties
index 73d41ed37..7aaa6d3a4 100644
--- a/plugins/org.eclipse.uml2.uml/plugin.properties
+++ b/plugins/org.eclipse.uml2.uml/plugin.properties
@@ -8,7 +8,7 @@
# IBM - initial API and implementation
# Kenn Hussey (Embarcadero Technologies) - 156879, 215488, 213218, 204200
# Kenn Hussey (CEA) - 327039, 351774, 373709, 388636, 295864, 397140, 316165, 322715, 80307
-# Christian W. Damus (CEA) - 373643, 374012, 163556, 409396
+# Christian W. Damus (CEA) - 373643, 374012, 163556, 409396, 180744
#
# NLS_MESSAGEFORMAT_VAR
@@ -175,6 +175,8 @@ _UI_Ecore2UMLConverter_ReportDocumentationAnnotation_diagnostic = Element ''{0}'
_UI_Ecore2UMLConverter_ProcessBodyAnnotation_diagnostic = Added a(n) ''{1}'' body condition to operation ''{0}'' based on a body annotation.
_UI_Ecore2UMLConverter_ProcessDocumentationAnnotation_diagnostic = Added a comment to element ''{0}'' based on a documentation annotation.
+_UI_TemplateExpander_ReportMissingParameterSubstitution_diagnostic = Parametered element ''{0}'' has no substitution in binding of template ''{1}''.
+
_UI_Options_label = Options
_UI_Discard_label = Discard
@@ -208,6 +210,8 @@ _UI_UnionAnnotations_label = Union Annotations
_UI_BodyAnnotations_label = Body Annotations
_UI_DocumentationAnnotations_label = Documentation Annotations
+_UI_MissingParameterSubstitutions_label = Missing Parameter Substitutions
+
_UI_UML_content_type = UML2 UML File
_UI_UML_2_0_0_content_type = UML2 2.0.0 UML File
_UI_UML_2_1_0_content_type = UML2 2.1.0 UML File
diff --git a/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java
index e9affc05c..62a6b2801 100644
--- a/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java
+++ b/plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java
@@ -12,7 +12,7 @@
* Kenn Hussey - 286329, 313601, 314971, 344907, 236184, 335125
* Kenn Hussey (CEA) - 327039, 358792, 364419, 366350, 307343, 382637, 273949, 389542, 389495, 316165, 392833, 399544, 322715, 163556, 212765, 397324, 204658, 408612, 411731, 269598
* Yann Tanguy (CEA) - 350402
- * Christian W. Damus (CEA) - 392833, 251963, 405061, 409396, 176998
+ * Christian W. Damus (CEA) - 392833, 251963, 405061, 409396, 176998, 180744
*
*/
package org.eclipse.uml2.uml.util;
@@ -92,6 +92,7 @@ import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.InterfaceRealization;
import org.eclipse.uml2.uml.LiteralInteger;
+import org.eclipse.uml2.uml.LiteralString;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.MultiplicityElement;
@@ -110,6 +111,7 @@ import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.RedefinableElement;
import org.eclipse.uml2.uml.Stereotype;
+import org.eclipse.uml2.uml.StringExpression;
import org.eclipse.uml2.uml.StructuralFeature;
import org.eclipse.uml2.uml.TemplateBinding;
import org.eclipse.uml2.uml.TemplateParameter;
@@ -301,6 +303,65 @@ public class UMLUtil
}
}
+ protected class ImplicitAssociationNameMatcher
+ extends EClassMatcher {
+
+ private final String name;
+
+ ImplicitAssociationNameMatcher(Association association) {
+ super(association);
+
+ name = getName(association);
+ }
+
+ @Override
+ public boolean matches(EObject otherEObject) {
+ boolean result = super.matches(otherEObject);
+
+ if (result) {
+ Association other = (Association) otherEObject;
+ result = safeEquals(name, getName(other));
+ }
+
+ return result;
+ }
+
+ protected String getName(Association association) {
+ String result = association.getName();
+
+ if (result == null) {
+ StringBuilder buf = new StringBuilder();
+
+ buf.append('A');
+ for (Property end : association.getMemberEnds()) {
+ buf.append('_');
+ buf.append(getName(end));
+ }
+
+ result = buf.toString();
+ }
+
+ return result;
+ }
+
+ protected String getName(Property end) {
+ String result = end.getName();
+
+ if ((result == null) && (end.getType() != null)) {
+ result = end.getType().getName();
+
+ if (!UML2Util.isEmpty(result)) {
+ result = String.format(
+ "%s%s", //$NON-NLS-1$
+ Character.toLowerCase(result.charAt(0)),
+ result.substring(1));
+ }
+ }
+
+ return result;
+ }
+ }
+
protected class KeyMatcher
extends EClassMatcher {
@@ -532,8 +593,20 @@ public class UMLUtil
*/
public static final int INDISTINGUISHABLE_CLASSIFIER = DIAGNOSTIC_CODE_OFFSET + 11;
- protected org.eclipse.uml2.uml.Package receivingPackage = null;
+ protected TemplateableElement receivingElement = null;
+ protected Collection<? extends TemplateableElement> mergedElements = null;
+
+ /**
+ * @deprecated Use the {@link #receivingElement}, instead.
+ */
+ @Deprecated
+ protected org.eclipse.uml2.uml.Package receivingPackage = null;
+
+ /**
+ * @deprecated Use the {@link #mergedElements}, instead
+ */
+ @Deprecated
protected Collection<org.eclipse.uml2.uml.Package> mergedPackages = null;
protected final Map<EObject, List<EObject>> resultingToMergedEObjectMap = new LinkedHashMap<EObject, List<EObject>>();
@@ -590,8 +663,8 @@ public class UMLUtil
private StringBuffer appendResultingQName(StringBuffer resultingQName,
EObject eObject, QualifiedTextProvider qualifiedTextProvider) {
- eObject = mergedPackages.contains(eObject)
- ? receivingPackage
+ eObject = mergedElements.contains(eObject)
+ ? receivingElement
: eObject;
EObject eContainer = eObject.eContainer();
@@ -709,7 +782,7 @@ public class UMLUtil
if (eObject != copyEObject) {
- if (copyEObject == receivingPackage) {
+ if (copyEObject == receivingElement) {
return;
} else if (resultingToMergedEObjectMap.containsKey(copyEObject)) {
@@ -894,6 +967,10 @@ public class UMLUtil
}
}
+ protected EObjectMatcher getAssociationMatcher(Association association) {
+ return new NameMatcher(association);
+ }
+
@Override
protected EObject createCopy(EObject eObject) {
return new UMLSwitch<EObject>() {
@@ -901,8 +978,8 @@ public class UMLUtil
@Override
public EObject caseAssociation(Association association) {
Association matchingAssociation = (Association) findEObject(
- getMatchCandidates(association), new NameMatcher(
- association));
+ getMatchCandidates(association),
+ getAssociationMatcher(association));
return matchingAssociation == null
? super.caseAssociation(association)
@@ -954,7 +1031,7 @@ public class UMLUtil
public EObject caseDirectedRelationship(
DirectedRelationship directedRelationship) {
- if (mergedPackages.containsAll(directedRelationship
+ if (mergedElements.containsAll(directedRelationship
.getTargets())) {
return directedRelationship;
@@ -1061,8 +1138,8 @@ public class UMLUtil
public EObject casePackage(org.eclipse.uml2.uml.Package package_) {
org.eclipse.uml2.uml.Package matchingPackage = null;
- if (mergedPackages.contains(package_)) {
- matchingPackage = receivingPackage;
+ if (mergedElements.contains(package_)) {
+ matchingPackage = (org.eclipse.uml2.uml.Package) receivingElement;
} else {
matchingPackage = (org.eclipse.uml2.uml.Package) findEObject(
getMatchCandidates(package_), new NameMatcher(
@@ -1244,7 +1321,7 @@ public class UMLUtil
protected void updateReferences() {
for (Iterator<EObject> iter = EcoreUtil.getAllContents(Collections
- .singleton(receivingPackage)); iter.hasNext();) {
+ .singleton(receivingElement)); iter.hasNext();) {
EObject next = iter.next();
@@ -1818,8 +1895,14 @@ public class UMLUtil
.get(OPTION__EMPTY_QUALIFIED_NAMES)) && diagnostics != null) {
EList<org.eclipse.uml2.uml.Package> packages = new UniqueEList<org.eclipse.uml2.uml.Package>();
- packages.add(receivingPackage);
- packages.addAll(mergedPackages);
+
+ if (receivingElement instanceof org.eclipse.uml2.uml.Package) {
+ packages
+ .add((org.eclipse.uml2.uml.Package) receivingElement);
+ packages.addAll(EcoreUtil
+ .<org.eclipse.uml2.uml.Package> getObjectsByType(
+ mergedElements, UMLPackage.Literals.PACKAGE));
+ }
for (org.eclipse.uml2.uml.Package package_ : packages) {
@@ -1853,8 +1936,14 @@ public class UMLUtil
&& diagnostics != null) {
EList<org.eclipse.uml2.uml.Package> packages = new UniqueEList<org.eclipse.uml2.uml.Package>();
- packages.add(receivingPackage);
- packages.addAll(mergedPackages);
+
+ if (receivingElement instanceof org.eclipse.uml2.uml.Package) {
+ packages
+ .add((org.eclipse.uml2.uml.Package) receivingElement);
+ packages.addAll(EcoreUtil
+ .<org.eclipse.uml2.uml.Package> getObjectsByType(
+ mergedElements, UMLPackage.Literals.PACKAGE));
+ }
for (org.eclipse.uml2.uml.Package package_ : packages) {
EList<Type> ownedTypes = package_.getOwnedTypes();
@@ -2127,22 +2216,84 @@ public class UMLUtil
org.eclipse.uml2.uml.Package package_,
Map<String, String> options, DiagnosticChain diagnostics,
Map<Object, Object> context) {
- receivingPackage = package_;
- mergedPackages = getAllMergedPackages(package_);
+ receivingPackage = package_; // for compatibility
+ mergedPackages = getAllMergedPackages(package_); // compatibility
- copyAll(mergedPackages);
- copyReferences();
+ return merge(receivingPackage, mergedPackages, options,
+ diagnostics, context);
+ }
- updateReferences();
+ /**
+ * Queries whether the current operation is a package merge, or some
+ * derivative.
+ *
+ * @return whether I am a package merge operation ({@code true}) or
+ * something else ({@false})
+ *
+ * @since 4.2
+ */
+ protected boolean isPackageMerge() {
+ // N.B.: Don't test whether receivingElement is a package, because
+ // that will be true in the case of expanding a package template!
+ return receivingPackage != null;
+ }
- for (Iterator<PackageMerge> packageMerges = receivingPackage
- .getPackageMerges().iterator(); packageMerges.hasNext();) {
+ /**
+ * Cleans up (removing or otherwise trimming as necessary) the
+ * relationships on the {@link #receivingElement} that define the merges
+ * that were performed.
+ *
+ * @since 4.2
+ */
+ protected void cleanupMergeRelationships() {
+ if (isPackageMerge()) {
+ // we ran a package merge. Destroy the merge relationships
+ for (Iterator<PackageMerge> packageMerges = receivingPackage
+ .getPackageMerges().iterator(); packageMerges.hasNext();) {
- PackageMerge packageMerge = packageMerges.next();
- packageMerges.remove();
- packageMerge.destroy();
+ PackageMerge packageMerge = packageMerges.next();
+ packageMerges.remove();
+ packageMerge.destroy();
+ }
}
+ }
+
+ /**
+ * Merges the contents of (all of) the elements merged by the specified
+ * package using the specified options, reporting problems to the
+ * specified diagnostics, within the specified context.
+ *
+ * @param package_
+ * The receiving package.
+ * @param options
+ * The options to use.
+ * @param diagnostics
+ * The chain of diagnostics to which problems are to be
+ * appended.
+ * @param context
+ * The cache of context-specific information.
+ *
+ * @return A traceability map from resulting elements to merged
+ * elements.
+ *
+ * @since 4.2
+ */
+ protected Map<EObject, List<EObject>> merge(
+ TemplateableElement receivingElement,
+ Collection<? extends TemplateableElement> mergedElements,
+ Map<String, String> options, DiagnosticChain diagnostics,
+ Map<Object, Object> context) {
+
+ this.receivingElement = receivingElement;
+ this.mergedElements = mergedElements;
+
+ copyAll(mergedElements);
+ copyReferences();
+
+ updateReferences();
+
+ cleanupMergeRelationships();
if (options != null) {
processOptions(options, diagnostics, context);
@@ -2153,6 +2304,562 @@ public class UMLUtil
}
/**
+ * A specialized copier that "expands" the {@linkplain TemplateBinding
+ * bindings} of templates modeled by {@link TemplateableElement}s to apply
+ * the bindings' {@linkplain TemplateBinding#getParameterSubstitutions()
+ * parameter substitutions} within a bound element. The template bindings
+ * are then destroyed as it is effectively replaced by a realization of the
+ * template; this is in many ways similar to the way that the
+ * {@link PackageMerger} works to effect a
+ * {@link org.eclipse.uml2.uml.Package}'s {@link PackageMerge}s.
+ *
+ * @since 4.2
+ */
+ public static class TemplateExpander
+ extends PackageMerger {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The option for handling cases where a template binding does not have
+ * a substitution for some parameter and that parameter does not have a
+ * default substitution. Supported choices are {@code OPTION__IGNORE}
+ * and {@code OPTION__REPORT}.
+ */
+ public static final String OPTION__MISSING_PARAMETER_SUBSTITUTIONS = "MISSING_PARAMETER_SUBSTITUTIONS"; //$NON-NLS-1$
+
+ private static final int DIAGNOSTIC_CODE_OFFSET = 4000;
+
+ /**
+ * The diagnostic code for cases where parameter substitutions are
+ * missing.
+ */
+ public static final int MISSING_PARAMETER_SUBSTITUTION = DIAGNOSTIC_CODE_OFFSET + 1;
+
+ protected TemplateBinding binding;
+
+ protected Map<ParameterableElement, ParameterableElement> substitutionMap = new java.util.HashMap<ParameterableElement, ParameterableElement>();
+
+ protected TemplateParameterSubstitution findSubstitution(
+ TemplateParameter templateParameter) {
+
+ TemplateParameterSubstitution result = null;
+
+ for (TemplateParameterSubstitution substitution : binding
+ .getParameterSubstitutions()) {
+
+ if (substitution.getFormal() == templateParameter) {
+ result = substitution;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ protected ParameterableElement getSubstitution(
+ ParameterableElement parameterableElement) {
+
+ ParameterableElement result = substitutionMap
+ .get(parameterableElement);
+
+ if (result == null) {
+ TemplateParameter parameter = parameterableElement
+ .getTemplateParameter();
+
+ if (parameter != null) {
+ // it's exposed as a template parameter. Substitute it
+ TemplateParameterSubstitution substitution = findSubstitution(parameter);
+ if (substitution != null) {
+ result = substitution.getActual();
+ substitutionMap.put(parameterableElement, result);
+ } else {
+ // look for a default
+ result = parameter.getDefault();
+ if (result != null) {
+ substitutionMap.put(parameterableElement, result);
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ protected void mergeNamedElement_Name(
+ NamedElement receivingNamedElement,
+ NamedElement mergedNamedElement) {
+
+ // only merge names if the substituting element doesn't have a name
+ // (this covers cases where the actual parameter is an object owned
+ // by the bound element and the formal parameter is likewise owned
+ // by the template, as in the UML Spec's package template example)
+ if (!receivingNamedElement.isSetName()
+ && mergedNamedElement.isSetName()) {
+
+ receivingNamedElement.setName(mergedNamedElement.getName());
+ }
+ }
+
+ protected void mergeLiteralString_Value(
+ LiteralString receivingLiteralString,
+ LiteralString mergedLiteralString) {
+
+ // only merge string values (which may be elements of name
+ // expressions)
+ // if the substituting string doesn't have a value
+ if (!receivingLiteralString.isSetValue()
+ && mergedLiteralString.isSetValue()) {
+
+ receivingLiteralString.setValue(mergedLiteralString.getValue());
+ }
+ }
+
+ @Override
+ protected void copyAttribute(EAttribute eAttribute, EObject eObject,
+ EObject copyEObject) {
+
+ if (eObject != copyEObject) {
+ if (copyEObject == receivingElement) {
+ return;
+ } else if (eAttribute == UMLPackage.Literals.NAMED_ELEMENT__NAME) {
+ // don't just blindly overwrite the names of objects matched
+ // in the containment-copy phase because elements owned by
+ // the template (not by its parameters) may be exposed by
+ // parameters
+ mergeNamedElement_Name((NamedElement) copyEObject,
+ (NamedElement) eObject);
+ } else if (eAttribute == UMLPackage.Literals.LITERAL_STRING__VALUE) {
+ // don't just blindly overwrite the values of strings
+ // matched
+ // in the containment-copy phase to allow substitution of
+ // string components in name expressions
+ mergeLiteralString_Value((LiteralString) copyEObject,
+ (LiteralString) eObject);
+ } else {
+ super.copyAttribute(eAttribute, eObject, copyEObject);
+ }
+ }
+ }
+
+ @Override
+ protected void copyContainment(EReference eReference, EObject eObject,
+ EObject copyEObject) {
+
+ // don't copy the template signatures that we're expanding, nor
+ // recursive bindings
+ if ((eObject != copyEObject)
+ && (eReference != UMLPackage.Literals.TEMPLATEABLE_ELEMENT__OWNED_TEMPLATE_SIGNATURE)
+ && (eReference != UMLPackage.Literals.TEMPLATEABLE_ELEMENT__TEMPLATE_BINDING)) {
+
+ if (eReference.isMany()) {
+ @SuppressWarnings("unchecked")
+ List<EObject> sourceList = (List<EObject>) eObject
+ .eGet(eReference);
+ @SuppressWarnings("unchecked")
+ InternalEList<EObject> targetList = (InternalEList<EObject>) copyEObject
+ .eGet(getTarget(eReference));
+
+ for (EObject childEObject : sourceList) {
+ EObject copyChildEObject = copy(childEObject);
+
+ if (childEObject != copyChildEObject) {
+ targetList.addUnique(copyChildEObject);
+ }
+ }
+ } else {
+ EObject childEObject = (EObject) eObject.eGet(eReference);
+ EObject copyChildEObject = childEObject == null
+ ? null
+ : copy(childEObject);
+
+ if (childEObject != copyChildEObject) {
+ copyEObject.eSet(getTarget(eReference),
+ copyChildEObject);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected EObjectMatcher getAssociationMatcher(Association association) {
+ return new ImplicitAssociationNameMatcher(association);
+ }
+
+ @Override
+ protected EObject createCopy(EObject eObject) {
+ return new UMLSwitch<EObject>() {
+
+ @Override
+ public EObject caseParameterableElement(
+ ParameterableElement object) {
+ EObject result;
+
+ ParameterableElement substitution = getSubstitution(object);
+ if (substitution != null) {
+ // it's exposed as a template parameter. Substitute it
+ result = substitution;
+ } else {
+ result = super.caseParameterableElement(object);
+ }
+
+ return result;
+ }
+
+ @Override
+ public EObject caseTemplateableElement(
+ TemplateableElement object) {
+ EObject result = null;
+
+ if (mergedElements.contains(object)) {
+ result = receivingElement;
+ }
+
+ if (result == null) {
+ result = super.caseTemplateableElement(object);
+ }
+
+ return result;
+ }
+
+ protected EObject basicCreateCopy(EObject element) {
+ EObject result = null;
+
+ if (element instanceof ParameterableElement) {
+ ParameterableElement parameterable = (ParameterableElement) element;
+ ParameterableElement sub = getSubstitution(parameterable);
+
+ if (sub != null) {
+ result = sub;
+ }
+ }
+
+ if (result == null) {
+ result = TemplateExpander.super
+ .createCopy((EObject) element);
+ }
+
+ return result;
+ }
+
+ @Override
+ public EObject defaultCase(EObject eObject) {
+ Element baseElement = getBaseElement(eObject);
+
+ return baseElement == null
+ ? basicCreateCopy(eObject)
+ : applyStereotype((Element) get(baseElement),
+ getTarget(eObject.eClass()));
+ }
+ }.doSwitch(eObject);
+ }
+
+ @Override
+ public void copyReferences() {
+ // before we copy references, now that the content trees have been
+ // merged, we can find and establish the default parameter
+ // substitution mappings that will be needed to correctly map
+ // references
+ initializeDefaultParameterSubstitutionMappings();
+
+ super.copyReferences();
+ }
+
+ private void initializeDefaultParameterSubstitutionMappings() {
+ for (TemplateableElement template : mergedElements) {
+ for (TemplateParameter parameter : template
+ .getOwnedTemplateSignature().getParameters()) {
+
+ ParameterableElement element = parameter
+ .getParameteredElement();
+
+ if ((element != null) && (parameter.getDefault() != null)) {
+ ParameterableElement sub = getSubstitution(element);
+ if (sub == null) {
+ ParameterableElement default_ = parameter
+ .getDefault();
+ EObject copy = get(default_);
+ if (copy != null) {
+ // map the parametered element to the copy of
+ // the default to effect the substitution
+ put(element, copy);
+ } else {
+ // map the parametered element to the (uncopied)
+ // default to effect the substitution
+ put(element, default_);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ protected void processMissingParameterSubstitutions(
+ Map<String, String> options, DiagnosticChain diagnostics,
+ Map<Object, Object> context) {
+
+ for (TemplateableElement template : mergedElements) {
+ for (TemplateParameter parameter : template
+ .getOwnedTemplateSignature().getParameters()) {
+
+ ParameterableElement element = parameter
+ .getParameteredElement();
+
+ if ((element != null) && (parameter.getDefault() == null)) {
+ ParameterableElement sub = getSubstitution(element);
+ if (sub == null) {
+ if (OPTION__REPORT.equals(options
+ .get(OPTION__MISSING_PARAMETER_SUBSTITUTIONS))
+ && diagnostics != null) {
+
+ diagnostics
+ .add(new BasicDiagnostic(
+ Diagnostic.WARNING,
+ UMLValidator.DIAGNOSTIC_SOURCE,
+ MISSING_PARAMETER_SUBSTITUTION,
+ UMLPlugin.INSTANCE
+ .getString(
+ "_UI_TemplateExpander_ReportMissingParameterSubstitution_diagnostic", //$NON-NLS-1$
+ getMessageSubstitutions(
+ context, element, template)),
+ new Object[]{template, element}));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Replace named element names with (template-expanded) name expressions
+ * where appropriate.
+ */
+ protected void processNameExpressions() {
+ for (Iterator<?> iter = EcoreUtil.getAllContents(Collections
+ .singleton(receivingElement)); iter.hasNext();) {
+
+ Object next = iter.next();
+ if (next instanceof NamedElement) {
+ NamedElement namedElement = (NamedElement) next;
+ StringExpression sexp = namedElement.getNameExpression();
+ if (sexp != null) {
+ namedElement.setName(sexp.stringValue());
+ sexp.destroy();
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void processOptions(Map<String, String> options,
+ DiagnosticChain diagnostics, Map<Object, Object> context) {
+
+ // this is not optional
+ processNameExpressions();
+
+ if (!OPTION__IGNORE.equals(options
+ .get(OPTION__MISSING_PARAMETER_SUBSTITUTIONS))) {
+
+ processMissingParameterSubstitutions(options, diagnostics,
+ context);
+ }
+
+ super.processOptions(options, diagnostics, context);
+ }
+
+ @Override
+ protected void cleanupMergeRelationships() {
+ super.cleanupMergeRelationships();
+
+ binding.destroy();
+ }
+
+ private void initializeParameteredElementMappings() {
+ TemplateSignature signature = binding.getSignature();
+
+ if (signature != null) {
+ for (TemplateParameter parameter : signature.getParameters()) {
+
+ ParameterableElement element = parameter
+ .getParameteredElement();
+
+ if (element != null) {
+ ParameterableElement sub = getSubstitution(element);
+ if (sub != null) {
+ put(element, sub);
+ } // defaults handled before copy-references step
+ }
+ }
+ }
+ }
+
+ protected Map<EObject, List<EObject>> expand(TemplateBinding binding,
+ TemplateableElement receivingElement,
+ final Map<String, String> options,
+ final DiagnosticChain diagnostics,
+ final Map<Object, Object> context) {
+
+ this.binding = binding;
+
+ // initialize some mappings for the parameter substitutions
+ initializeParameteredElementMappings();
+
+ TemplateableElement template = binding.getSignature().getTemplate();
+ return merge(receivingElement, Collections.singleton(template),
+ options, diagnostics, context);
+ }
+
+ public Map<EObject, List<EObject>> expand(
+ TemplateableElement boundElement,
+ final Map<String, String> options,
+ final DiagnosticChain diagnostics,
+ final Map<Object, Object> context) {
+
+ if (boundElement.getTemplateBindings().size() == 1) {
+ // the nice case: only binding one template
+ TemplateBinding binding = boundElement.getTemplateBindings()
+ .get(0);
+ return expand(binding, binding.getBoundElement(), options,
+ diagnostics, context);
+ } else {
+ // apply the UML rules for multiple template bindings
+
+ // copy the bindings because we destroy them as we go
+ final List<TemplateBinding> bindings = new java.util.ArrayList<TemplateBinding>(
+ boundElement.getTemplateBindings());
+
+ return new UMLSwitch<Map<EObject, List<EObject>>>() {
+
+ private Map<EObject, List<EObject>> merge(
+ Map<EObject, List<EObject>> result,
+ Map<EObject, List<EObject>> map) {
+ for (Map.Entry<EObject, List<EObject>> next : map
+ .entrySet()) {
+ List<EObject> list = result.get(next.getKey());
+ if (list == null) {
+ result.put(
+ next.getKey(),
+ new java.util.ArrayList<EObject>(next
+ .getValue()));
+ } else {
+ list.addAll(next.getValue());
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public Map<EObject, List<EObject>> caseTemplateableElement(
+ TemplateableElement object) {
+
+ // TODO: UML does not specify how to handle multiple
+ // bindings of StringExpressions and Operations, so
+ // just process the first binding for now
+ return expand(bindings.get(0), object, options,
+ diagnostics, context);
+ }
+
+ @Override
+ public Map<EObject, List<EObject>> caseClassifier(
+ Classifier object) {
+ // UML specifies that we create intermediate expansions
+ // and specialize each
+ for (TemplateBinding binding : bindings) {
+ Classifier intermediate = (Classifier) EcoreUtil
+ .create(object.eClass());
+
+ // have to use a distinct template-expander for each
+ // because multiple bindings to the same template
+ // are allowed and an EcoreUtil.Copier can only copy
+ // an element once
+ merge(resultingToMergedEObjectMap,
+ new TemplateExpander()
+ .expand(binding, intermediate, options,
+ diagnostics, context));
+
+ // add the intermediate result to the model
+ addAnonymousGeneral(intermediate, object);
+ }
+
+ return resultingToMergedEObjectMap;
+ }
+
+ private void addAnonymousGeneral(final Classifier general,
+ Classifier special) {
+ Classifier added = new UMLSwitch<Classifier>() {
+
+ @Override
+ public Classifier caseClassifier(Classifier object) {
+ // cop out
+ object.getNearestPackage()
+ .getPackagedElements().add(general);
+ return general;
+ }
+
+ @Override
+ public Classifier caseClass(
+ org.eclipse.uml2.uml.Class object) {
+ object.getNestedClassifiers().add(general);
+ return general;
+ }
+
+ @Override
+ public Classifier caseInterface(Interface object) {
+ object.getNestedClassifiers().add(general);
+ return general;
+ }
+ }.doSwitch(special);
+ added.unsetName(); // make sure it's anonymous
+ special.createGeneralization(general); // specialize it
+ }
+
+ @Override
+ public Map<EObject, List<EObject>> casePackage(
+ org.eclipse.uml2.uml.Package object) {
+ // UML specifies that we create intermediate expansions
+ // and merge each. But because we are a package merger,
+ // we can optimize out the intermediate packages
+
+ for (TemplateBinding binding : bindings) {
+ org.eclipse.uml2.uml.Package intermediate = (org.eclipse.uml2.uml.Package) EcoreUtil
+ .create(object.eClass());
+
+ // have to use a distinct template-expander for each
+ // because multiple bindings to the same template
+ // are allowed and an EcoreUtil.Copier can only copy
+ // an element once
+ merge(resultingToMergedEObjectMap,
+ new TemplateExpander()
+ .expand(binding, intermediate, options,
+ diagnostics, context));
+
+ // merge the intermediate result
+ object.createPackageMerge(intermediate);
+ }
+
+ // merge the intermediate results (this is a *real*
+ // package merge)
+ merge(resultingToMergedEObjectMap, new PackageMerger() {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected EObjectMatcher getAssociationMatcher(
+ Association association) {
+ return new ImplicitAssociationNameMatcher(
+ association);
+ }
+ }.merge(object, options, diagnostics, context));
+
+ return resultingToMergedEObjectMap;
+ }
+ }.doSwitch(boundElement);
+ }
+ }
+ }
+
+ /**
* A converter that converts UML elements to representative Ecore model
* elements.
*/
@@ -2219,7 +2926,8 @@ public class UMLUtil
}
}
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Boolean
+ .getBoolean(UML2EcoreConverter.class.getName() + ".DEBUG"); //$NON-NLS-1$
/**
* The option for handling cases where an Ecore tagged value is
@@ -10179,6 +10887,14 @@ public class UMLUtil
org.eclipse.uml2.uml.Package package_,
Map<String, String> options) {
+ options = defaultPackageMergeOptions(options, OPTION__IGNORE);
+
+ return merge(package_, options, null, null);
+ }
+
+ private static Map<String, String> defaultPackageMergeOptions(
+ Map<String, String> options, String defaultValue) {
+
if (options == null) {
options = new HashMap<String, String>();
}
@@ -10187,53 +10903,53 @@ public class UMLUtil
.containsKey(PackageMerger.OPTION__DIFFERENT_PROPERTY_STATICITY)) {
options.put(PackageMerger.OPTION__DIFFERENT_PROPERTY_STATICITY,
- OPTION__IGNORE);
+ defaultValue);
}
if (!options
.containsKey(PackageMerger.OPTION__DIFFERENT_PROPERTY_UNIQUENESS)) {
options.put(PackageMerger.OPTION__DIFFERENT_PROPERTY_UNIQUENESS,
- OPTION__IGNORE);
+ defaultValue);
}
if (!options
.containsKey(PackageMerger.OPTION__REDUNDANT_GENERALIZATIONS)) {
options.put(PackageMerger.OPTION__REDUNDANT_GENERALIZATIONS,
- OPTION__IGNORE);
+ defaultValue);
}
if (!options.containsKey(PackageMerger.OPTION__IMPLICIT_REDEFINITIONS)) {
options.put(PackageMerger.OPTION__IMPLICIT_REDEFINITIONS,
- OPTION__IGNORE);
+ defaultValue);
}
if (!options.containsKey(PackageMerger.OPTION__INVALID_REDEFINITIONS)) {
options.put(PackageMerger.OPTION__INVALID_REDEFINITIONS,
- OPTION__IGNORE);
+ defaultValue);
}
if (!options.containsKey(PackageMerger.OPTION__INVALID_SUBSETS)) {
- options.put(PackageMerger.OPTION__INVALID_SUBSETS, OPTION__IGNORE);
+ options.put(PackageMerger.OPTION__INVALID_SUBSETS, defaultValue);
}
if (!options.containsKey(PackageMerger.OPTION__EMPTY_UNIONS)) {
- options.put(PackageMerger.OPTION__EMPTY_UNIONS, OPTION__IGNORE);
+ options.put(PackageMerger.OPTION__EMPTY_UNIONS, defaultValue);
}
if (!options
.containsKey(PackageMerger.OPTION__ASSOCIATION_SPECIALIZATIONS)) {
options.put(PackageMerger.OPTION__ASSOCIATION_SPECIALIZATIONS,
- OPTION__IGNORE);
+ defaultValue);
}
if (!options.containsKey(PackageMerger.OPTION__CAPABILITIES)) {
- options.put(PackageMerger.OPTION__CAPABILITIES, OPTION__IGNORE);
+ options.put(PackageMerger.OPTION__CAPABILITIES, defaultValue);
}
- return merge(package_, options, null, null);
+ return options;
}
/**
@@ -10257,61 +10973,81 @@ public class UMLUtil
Map<String, String> options, DiagnosticChain diagnostics,
Map<Object, Object> context) {
- if (options == null) {
- options = new HashMap<String, String>();
- }
-
- if (!options
- .containsKey(PackageMerger.OPTION__DIFFERENT_PROPERTY_STATICITY)) {
+ options = defaultPackageMergeOptions(options, OPTION__REPORT);
- options.put(PackageMerger.OPTION__DIFFERENT_PROPERTY_STATICITY,
- OPTION__REPORT);
- }
+ return new PackageMerger().merge(package_, options, diagnostics, context);
+ }
- if (!options
- .containsKey(PackageMerger.OPTION__DIFFERENT_PROPERTY_UNIQUENESS)) {
+ /**
+ * Applies all of the {@linkplain TemplateableElement#getTemplateBindings()
+ * template bindings} of an element, copying the structure of the templates
+ * that it binds to and applying parameter substitutions. If a supported
+ * option is not specified, it will be defaulted to
+ * <code>OPTION__IGNORE</code>.
+ *
+ * @param templateableElement
+ * The templateable element to expand by applying its bound
+ * templates
+ * @param options
+ * The options to use.
+ * @return A traceability map from resulting elements to template elements.
+ *
+ * @since 4.2
+ */
+ public static Map<EObject, List<EObject>> expand(
+ TemplateableElement templateableElement, Map<String, String> options) {
- options.put(PackageMerger.OPTION__DIFFERENT_PROPERTY_UNIQUENESS,
- OPTION__REPORT);
- }
+ options = defaultPackageMergeOptions(options, OPTION__IGNORE);
if (!options
- .containsKey(PackageMerger.OPTION__REDUNDANT_GENERALIZATIONS)) {
-
- options.put(PackageMerger.OPTION__REDUNDANT_GENERALIZATIONS,
- OPTION__REPORT);
- }
-
- if (!options.containsKey(PackageMerger.OPTION__IMPLICIT_REDEFINITIONS)) {
- options.put(PackageMerger.OPTION__IMPLICIT_REDEFINITIONS,
- OPTION__REPORT);
+ .containsKey(TemplateExpander.OPTION__MISSING_PARAMETER_SUBSTITUTIONS)) {
+ options.put(
+ TemplateExpander.OPTION__MISSING_PARAMETER_SUBSTITUTIONS,
+ OPTION__IGNORE);
}
- if (!options.containsKey(PackageMerger.OPTION__INVALID_REDEFINITIONS)) {
- options.put(PackageMerger.OPTION__INVALID_REDEFINITIONS,
- OPTION__REPORT);
- }
+ return expand(templateableElement, options, null, null);
+ }
- if (!options.containsKey(PackageMerger.OPTION__INVALID_SUBSETS)) {
- options.put(PackageMerger.OPTION__INVALID_SUBSETS, OPTION__REPORT);
- }
+ /**
+ * Applies all of the {@linkplain TemplateableElement#getTemplateBindings()
+ * template bindings} of an element, copying the structure of the templates
+ * that it binds to and applying parameter substitutions. If a supported
+ * option is not specified, it will be defaulted to
+ * <code>OPTION__REPORT</code>.
+ *
+ * @param boundElement
+ * The bound element to expand by applying its templates
+ * @param options
+ * The options to use.
+ * @param diagnostics
+ * The chain of diagnostics to which problems are to be appended.
+ * @param context
+ * The cache of context-specific information.
+ * @return A traceability map from resulting elements to template elements.
+ *
+ * @since 4.2
+ */
+ public static Map<EObject, List<EObject>> expand(
+ TemplateableElement boundElement,
+ Map<String, String> options, final DiagnosticChain diagnostics,
+ final Map<Object, Object> context) {
- if (!options.containsKey(PackageMerger.OPTION__EMPTY_UNIONS)) {
- options.put(PackageMerger.OPTION__EMPTY_UNIONS, OPTION__REPORT);
- }
+ options = defaultPackageMergeOptions(options, OPTION__REPORT);
if (!options
- .containsKey(PackageMerger.OPTION__ASSOCIATION_SPECIALIZATIONS)) {
-
- options.put(PackageMerger.OPTION__ASSOCIATION_SPECIALIZATIONS,
+ .containsKey(TemplateExpander.OPTION__MISSING_PARAMETER_SUBSTITUTIONS)) {
+ options.put(
+ TemplateExpander.OPTION__MISSING_PARAMETER_SUBSTITUTIONS,
OPTION__REPORT);
}
- if (!options.containsKey(PackageMerger.OPTION__CAPABILITIES)) {
- options.put(PackageMerger.OPTION__CAPABILITIES, OPTION__REPORT);
- }
+ // we're not really doing a package merge, so forget processing the
+ // "capabilities"
+ options.put(PackageMerger.OPTION__CAPABILITIES, OPTION__IGNORE);
- return new PackageMerger().merge(package_, options, diagnostics, context);
+ return new TemplateExpander().expand(boundElement, options,
+ diagnostics, context);
}
/**
diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744.profile.uml b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744.profile.uml
new file mode 100644
index 000000000..e49219b85
--- /dev/null
+++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744.profile.uml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<uml:Profile xmi:version="20110701" xmlns:xmi="http://www.omg.org/spec/XMI/20110701" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:uml="http://www.eclipse.org/uml2/4.0.0/UML" xmi:id="_SZzrkD3BEeOmXIxoEtuCrA" name="bug180744profile" URI="http://www.eclipse.org/uml2/tests/bug180744profile" metamodelReference="_p4otgD3BEeOmXIxoEtuCrA">
+ <eAnnotations xmi:id="_zzE9YD3BEeOmXIxoEtuCrA" source="http://www.eclipse.org/uml2/2.0.0/UML">
+ <contents xmi:type="ecore:EPackage" xmi:id="_zzE9YT3BEeOmXIxoEtuCrA" name="bug180744profile" nsURI="http://www.eclipse.org/uml2/tests/bug180744profile" nsPrefix="bug180744profile">
+ <eClassifiers xmi:type="ecore:EClass" xmi:id="_zzE9Yj3BEeOmXIxoEtuCrA" name="Limited">
+ <eAnnotations xmi:id="_zzE9Yz3BEeOmXIxoEtuCrA" source="http://www.eclipse.org/uml2/2.0.0/UML" references="_q2nYsD3BEeOmXIxoEtuCrA"/>
+ <eStructuralFeatures xmi:type="ecore:EReference" xmi:id="_zzE9ZD3BEeOmXIxoEtuCrA" name="base_Classifier" ordered="false" lowerBound="1">
+ <eType xmi:type="ecore:EClass" href="http://www.eclipse.org/uml2/4.0.0/UML#//Classifier"/>
+ </eStructuralFeatures>
+ </eClassifiers>
+ </contents>
+ </eAnnotations>
+ <packageImport xmi:id="_p4otgD3BEeOmXIxoEtuCrA">
+ <importedPackage xmi:type="uml:Model" href="pathmap://UML_METAMODELS/UML.metamodel.uml#_0"/>
+ </packageImport>
+ <packagedElement xmi:type="uml:Stereotype" xmi:id="_q2nYsD3BEeOmXIxoEtuCrA" name="Limited">
+ <ownedAttribute xmi:id="_vddoAT3BEeOmXIxoEtuCrA" name="base_Classifier" association="_vddA8D3BEeOmXIxoEtuCrA">
+ <type xmi:type="uml:Class" href="pathmap://UML_METAMODELS/UML.metamodel.uml#Classifier"/>
+ </ownedAttribute>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Extension" xmi:id="_vddA8D3BEeOmXIxoEtuCrA" name="Classifier_Limited" memberEnd="_vddoAD3BEeOmXIxoEtuCrA _vddoAT3BEeOmXIxoEtuCrA">
+ <ownedEnd xmi:type="uml:ExtensionEnd" xmi:id="_vddoAD3BEeOmXIxoEtuCrA" name="extension_Limited" type="_q2nYsD3BEeOmXIxoEtuCrA" aggregation="composite" association="_vddA8D3BEeOmXIxoEtuCrA"/>
+ </packagedElement>
+</uml:Profile>
diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744.uml b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744.uml
new file mode 100644
index 000000000..4ad7b424b
--- /dev/null
+++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744.uml
@@ -0,0 +1,200 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xmi:XMI xmi:version="20110701" xmlns:xmi="http://www.omg.org/spec/XMI/20110701" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bug180744profile="http://www.eclipse.org/uml2/tests/bug180744profile" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:uml="http://www.eclipse.org/uml2/4.0.0/UML" xsi:schemaLocation="http://www.eclipse.org/uml2/tests/bug180744profile Bug180744.profile.uml#_zzE9YT3BEeOmXIxoEtuCrA">
+ <uml:Package xmi:id="_uacM4DvhEeOFiu2NN-JcQw" name="templates" URI="http://www.eclipse.org/uml2/tests/bug180744">
+ <elementImport xmi:id="_4cDhUDwZEeO4Rq2NI2kHpA">
+ <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/>
+ </elementImport>
+ <elementImport xmi:id="_4cDhUTwZEeO4Rq2NI2kHpA">
+ <importedElement xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+ </elementImport>
+ <packagedElement xmi:type="uml:Enumeration" xmi:id="_nDaPwDvzEeO8DKN9IOhj1A" name="CollectionKind">
+ <ownedLiteral xmi:id="_xaUZMDvzEeO8DKN9IOhj1A" name="orderedset"/>
+ <ownedLiteral xmi:id="_0OtzcDvzEeO8DKN9IOhj1A" name="set"/>
+ <ownedLiteral xmi:id="_0e49cDvzEeO8DKN9IOhj1A" name="sequence"/>
+ <ownedLiteral xmi:id="_0kw78DvzEeO8DKN9IOhj1A" name="bag"/>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_zyIDQDvhEeOFiu2NN-JcQw" name="Collection">
+ <ownedTemplateSignature xmi:type="uml:RedefinableTemplateSignature" xmi:id="_9OTCwDvhEeOFiu2NN-JcQw" parameter="_CfvuQDviEeOFiu2NN-JcQw _JtPxkDv0EeO8DKN9IOhj1A">
+ <ownedParameter xmi:type="uml:ClassifierTemplateParameter" xmi:id="_CfvuQDviEeOFiu2NN-JcQw" parameteredElement="_FkjjEDviEeOFiu2NN-JcQw">
+ <ownedParameteredElement xmi:type="uml:Class" xmi:id="_FkjjEDviEeOFiu2NN-JcQw" name="E" templateParameter="_CfvuQDviEeOFiu2NN-JcQw"/>
+ </ownedParameter>
+ <ownedParameter xmi:id="_JtPxkDv0EeO8DKN9IOhj1A" default="_0kw78DvzEeO8DKN9IOhj1A" parameteredElement="_rL9HkDv1EeO8DKN9IOhj1A">
+ <ownedParameteredElement xmi:type="uml:EnumerationLiteral" xmi:id="_rL9HkDv1EeO8DKN9IOhj1A" name="K" templateParameter="_JtPxkDv0EeO8DKN9IOhj1A"/>
+ </ownedParameter>
+ </ownedTemplateSignature>
+ <ownedAttribute xmi:id="_8I-7UDvzEeO8DKN9IOhj1A" name="kind" isStatic="true" type="_nDaPwDvzEeO8DKN9IOhj1A">
+ <defaultValue xmi:type="uml:InstanceValue" xmi:id="_HhkkMDv0EeO8DKN9IOhj1A" type="_nDaPwDvzEeO8DKN9IOhj1A" instance="_rL9HkDv1EeO8DKN9IOhj1A"/>
+ </ownedAttribute>
+ <ownedOperation xmi:id="_j7_bkDviEeOFiu2NN-JcQw" name="add">
+ <ownedParameter xmi:id="_vV220DviEeOFiu2NN-JcQw" name="element" type="_FkjjEDviEeOFiu2NN-JcQw"/>
+ </ownedOperation>
+ <ownedOperation xmi:id="_ygql8DviEeOFiu2NN-JcQw" name="remove">
+ <ownedParameter xmi:id="_ygql8TviEeOFiu2NN-JcQw" name="element" type="_FkjjEDviEeOFiu2NN-JcQw"/>
+ </ownedOperation>
+ <ownedOperation xmi:id="_ytVI8DviEeOFiu2NN-JcQw" name="contains">
+ <ownedParameter xmi:id="_ytVI8TviEeOFiu2NN-JcQw" name="element" type="_FkjjEDviEeOFiu2NN-JcQw"/>
+ </ownedOperation>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_HrrLIDvjEeOFiu2NN-JcQw" name="Foo"/>
+ <packagedElement xmi:type="uml:Class" xmi:id="_tT2nYDwlEeOUNYI2YyBb_w" name="Bar"/>
+ <packagedElement xmi:type="uml:Class" xmi:id="_J-_RkDvjEeOFiu2NN-JcQw" name="BagOfFoos">
+ <ownedComment xmi:id="_nqAgQDv0EeO8DKN9IOhj1A">
+ <body>The collection kind template parameter defaults to 'bag'.</body>
+ </ownedComment>
+ <templateBinding xmi:id="_MCWQcDvjEeOFiu2NN-JcQw" signature="_9OTCwDvhEeOFiu2NN-JcQw">
+ <parameterSubstitution xmi:id="_NmT9kDvjEeOFiu2NN-JcQw" actual="_HrrLIDvjEeOFiu2NN-JcQw" formal="_CfvuQDviEeOFiu2NN-JcQw"/>
+ </templateBinding>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_s7UB0Dv0EeO8DKN9IOhj1A" name="ListOfBars">
+ <ownedComment xmi:id="_s7UB0Tv0EeO8DKN9IOhj1A">
+ <body>The collection kind template parameter is set explicitly to 'sequence'.</body>
+ </ownedComment>
+ <templateBinding xmi:id="_s7UB0jv0EeO8DKN9IOhj1A" signature="_9OTCwDvhEeOFiu2NN-JcQw">
+ <parameterSubstitution xmi:id="_s7UB0zv0EeO8DKN9IOhj1A" actual="_tT2nYDwlEeOUNYI2YyBb_w" formal="_CfvuQDviEeOFiu2NN-JcQw"/>
+ <parameterSubstitution xmi:id="_10bXkDv0EeO8DKN9IOhj1A" actual="_0e49cDvzEeO8DKN9IOhj1A" formal="_JtPxkDv0EeO8DKN9IOhj1A"/>
+ </templateBinding>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_plTSADwlEeOUNYI2YyBb_w" name="BagListOfFooBars">
+ <ownedComment xmi:id="_plTSATwlEeOUNYI2YyBb_w">
+ <body>No, it makes no sense to have a class that is both a BagOfFoos and a ListOfBars.</body>
+ </ownedComment>
+ <templateBinding xmi:id="_plTSAjwlEeOUNYI2YyBb_w" signature="_9OTCwDvhEeOFiu2NN-JcQw">
+ <parameterSubstitution xmi:id="_plTSAzwlEeOUNYI2YyBb_w" actual="_tT2nYDwlEeOUNYI2YyBb_w" formal="_CfvuQDviEeOFiu2NN-JcQw"/>
+ <parameterSubstitution xmi:id="_plTSBDwlEeOUNYI2YyBb_w" actual="_0e49cDvzEeO8DKN9IOhj1A" formal="_JtPxkDv0EeO8DKN9IOhj1A"/>
+ </templateBinding>
+ <templateBinding xmi:id="_CBLLADwmEeOUNYI2YyBb_w" signature="_9OTCwDvhEeOFiu2NN-JcQw">
+ <parameterSubstitution xmi:id="_CBLLATwmEeOUNYI2YyBb_w" actual="_HrrLIDvjEeOFiu2NN-JcQw" formal="_CfvuQDviEeOFiu2NN-JcQw"/>
+ </templateBinding>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Package" xmi:id="__u0QMDwMEeO4Rq2NI2kHpA" name="system">
+ <ownedTemplateSignature xmi:id="_EabNQDwZEeO4Rq2NI2kHpA" parameter="_GcWowDwZEeO4Rq2NI2kHpA _LI3bMDwZEeO4Rq2NI2kHpA _BJFoMDzxEeOkQ_NYnvAgYw _VqNi4DzxEeOkQ_NYnvAgYw">
+ <ownedParameter xmi:type="uml:ClassifierTemplateParameter" xmi:id="_GcWowDwZEeO4Rq2NI2kHpA" parameteredElement="_Fnn2oDwNEeO4Rq2NI2kHpA"/>
+ <ownedParameter xmi:type="uml:ClassifierTemplateParameter" xmi:id="_LI3bMDwZEeO4Rq2NI2kHpA" parameteredElement="_8jN14DwXEeO4Rq2NI2kHpA"/>
+ <ownedParameter xmi:id="_BJFoMDzxEeOkQ_NYnvAgYw" default="_JVm9sDzxEeOkQ_NYnvAgYw" parameteredElement="_ychGEDzwEeOkQ_NYnvAgYw">
+ <ownedDefault xmi:type="uml:LiteralString" xmi:id="_JVm9sDzxEeOkQ_NYnvAgYw" value=""/>
+ </ownedParameter>
+ <ownedParameter xmi:id="_VqNi4DzxEeOkQ_NYnvAgYw" default="_VqNi4TzxEeOkQ_NYnvAgYw" parameteredElement="_ShSDETzxEeOkQ_NYnvAgYw">
+ <ownedDefault xmi:type="uml:LiteralString" xmi:id="_VqNi4TzxEeOkQ_NYnvAgYw" value=""/>
+ </ownedParameter>
+ </ownedTemplateSignature>
+ <packagedElement xmi:type="uml:Class" xmi:id="_Fnn2oDwNEeO4Rq2NI2kHpA" name="Resource" templateParameter="_GcWowDwZEeO4Rq2NI2kHpA">
+ <ownedAttribute xmi:id="_L-f4IDwYEeO4Rq2NI2kHpA" name="kind" type="_8jN14DwXEeO4Rq2NI2kHpA" association="_Jv1BIDwYEeO4Rq2NI2kHpA"/>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_8jN14DwXEeO4Rq2NI2kHpA" name="ResourceKind" templateParameter="_LI3bMDwZEeO4Rq2NI2kHpA">
+ <ownedAttribute xmi:id="_Qhm28DwYEeO4Rq2NI2kHpA" name="resource" type="_Fnn2oDwNEeO4Rq2NI2kHpA" association="_Jv1BIDwYEeO4Rq2NI2kHpA">
+ <lowerValue xmi:type="uml:LiteralInteger" xmi:id="_SsxuADwYEeO4Rq2NI2kHpA"/>
+ <upperValue xmi:type="uml:LiteralUnlimitedNatural" xmi:id="_TKEuADwYEeO4Rq2NI2kHpA" value="*"/>
+ </ownedAttribute>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_LxCE4DwNEeO4Rq2NI2kHpA" name="Allocation">
+ <nameExpression xmi:id="_mx4D4DzwEeOkQ_NYnvAgYw" symbol="">
+ <operand xmi:type="uml:LiteralString" xmi:id="_ychGEDzwEeOkQ_NYnvAgYw" templateParameter="_BJFoMDzxEeOkQ_NYnvAgYw" value="${ResourceKind}"/>
+ <operand xmi:type="uml:LiteralString" xmi:id="_6UyC0DzwEeOkQ_NYnvAgYw" value="Allocation"/>
+ </nameExpression>
+ <ownedAttribute xmi:id="_w4sTADwYEeO4Rq2NI2kHpA" name="resource" type="_Fnn2oDwNEeO4Rq2NI2kHpA" association="_zkSWkDwYEeO4Rq2NI2kHpA"/>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_BPWN0DwYEeO4Rq2NI2kHpA" name="Request">
+ <nameExpression xmi:id="_ShSDEDzxEeOkQ_NYnvAgYw" symbol="">
+ <operand xmi:type="uml:LiteralString" xmi:id="_ShSDETzxEeOkQ_NYnvAgYw" templateParameter="_VqNi4DzxEeOkQ_NYnvAgYw" value="${ResourceKind}"/>
+ <operand xmi:type="uml:LiteralString" xmi:id="_ShSDEjzxEeOkQ_NYnvAgYw" value="Request"/>
+ </nameExpression>
+ <ownedAttribute xmi:id="_dTd_4DwYEeO4Rq2NI2kHpA" name="kind" type="_8jN14DwXEeO4Rq2NI2kHpA" association="_fllMcDwYEeO4Rq2NI2kHpA"/>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Association" xmi:id="_Jv1BIDwYEeO4Rq2NI2kHpA" memberEnd="_L-f4IDwYEeO4Rq2NI2kHpA _Qhm28DwYEeO4Rq2NI2kHpA"/>
+ <packagedElement xmi:type="uml:Association" xmi:id="_fllMcDwYEeO4Rq2NI2kHpA" memberEnd="_jMVQsDwYEeO4Rq2NI2kHpA _dTd_4DwYEeO4Rq2NI2kHpA">
+ <ownedEnd xmi:id="_jMVQsDwYEeO4Rq2NI2kHpA" type="_BPWN0DwYEeO4Rq2NI2kHpA" association="_fllMcDwYEeO4Rq2NI2kHpA">
+ <lowerValue xmi:type="uml:LiteralInteger" xmi:id="_kGqyADwYEeO4Rq2NI2kHpA"/>
+ <upperValue xmi:type="uml:LiteralUnlimitedNatural" xmi:id="_kwZrgDwYEeO4Rq2NI2kHpA" value="*"/>
+ </ownedEnd>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Association" xmi:id="_zkSWkDwYEeO4Rq2NI2kHpA" memberEnd="_1Osp8DwYEeO4Rq2NI2kHpA _w4sTADwYEeO4Rq2NI2kHpA">
+ <ownedEnd xmi:id="_1Osp8DwYEeO4Rq2NI2kHpA" type="_LxCE4DwNEeO4Rq2NI2kHpA" association="_zkSWkDwYEeO4Rq2NI2kHpA">
+ <lowerValue xmi:type="uml:LiteralInteger" xmi:id="_2e01EDwYEeO4Rq2NI2kHpA"/>
+ <upperValue xmi:type="uml:LiteralUnlimitedNatural" xmi:id="_3F41EDwYEeO4Rq2NI2kHpA" value="*"/>
+ </ownedEnd>
+ </packagedElement>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Package" xmi:id="_UzK9UDwZEeO4Rq2NI2kHpA" name="rental system">
+ <ownedComment xmi:id="_KvW7sDztEeOo3uNUxreuKg">
+ <body>Package template binding with owned classes in the bound package substituting
+for owned classes in the template.
+The parametered name-expression components are left at defaults.</body>
+ </ownedComment>
+ <templateBinding xmi:id="_pZBnADwZEeO4Rq2NI2kHpA" signature="_EabNQDwZEeO4Rq2NI2kHpA">
+ <parameterSubstitution xmi:id="_tbf98DwZEeO4Rq2NI2kHpA" actual="_ay8HYDwZEeO4Rq2NI2kHpA" formal="_GcWowDwZEeO4Rq2NI2kHpA"/>
+ <parameterSubstitution xmi:id="_xEon0DwZEeO4Rq2NI2kHpA" actual="_kxFykDwZEeO4Rq2NI2kHpA" formal="_LI3bMDwZEeO4Rq2NI2kHpA"/>
+ </templateBinding>
+ <packagedElement xmi:type="uml:Class" xmi:id="_ay8HYDwZEeO4Rq2NI2kHpA" name="Car">
+ <ownedAttribute xmi:id="_hAq08DwZEeO4Rq2NI2kHpA" name="mileage">
+ <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/>
+ </ownedAttribute>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_kxFykDwZEeO4Rq2NI2kHpA" name="CarSpec">
+ <ownedAttribute xmi:id="_kxFykTwZEeO4Rq2NI2kHpA" name="make">
+ <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+ </ownedAttribute>
+ <ownedAttribute xmi:id="_m6H7oDwZEeO4Rq2NI2kHpA" name="model">
+ <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+ </ownedAttribute>
+ </packagedElement>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Package" xmi:id="_vaUdMDzsEeOo3uNUxreuKg" name="deployment system">
+ <ownedComment xmi:id="_Q4FNcDztEeOo3uNUxreuKg">
+ <body>Package with multiple bindings to the same template.</body>
+ </ownedComment>
+ <templateBinding xmi:id="_vaUdMTzsEeOo3uNUxreuKg" signature="_EabNQDwZEeO4Rq2NI2kHpA">
+ <parameterSubstitution xmi:id="_vaUdMjzsEeOo3uNUxreuKg" actual="_vaUdNDzsEeOo3uNUxreuKg" formal="_GcWowDwZEeO4Rq2NI2kHpA"/>
+ <parameterSubstitution xmi:id="_vaUdMzzsEeOo3uNUxreuKg" actual="_vaUdNjzsEeOo3uNUxreuKg" formal="_LI3bMDwZEeO4Rq2NI2kHpA"/>
+ <parameterSubstitution xmi:id="_0PwI0DzxEeOkQ_NYnvAgYw" actual="_5-8gQDzxEeOkQ_NYnvAgYw" formal="_BJFoMDzxEeOkQ_NYnvAgYw">
+ <ownedActual xmi:type="uml:LiteralString" xmi:id="_5-8gQDzxEeOkQ_NYnvAgYw" value="Software"/>
+ </parameterSubstitution>
+ <parameterSubstitution xmi:id="_7hGPgDzxEeOkQ_NYnvAgYw" actual="_7hGPgTzxEeOkQ_NYnvAgYw" formal="_VqNi4DzxEeOkQ_NYnvAgYw">
+ <ownedActual xmi:type="uml:LiteralString" xmi:id="_7hGPgTzxEeOkQ_NYnvAgYw" value="Software"/>
+ </parameterSubstitution>
+ </templateBinding>
+ <templateBinding xmi:id="_C7iaEDztEeOo3uNUxreuKg" signature="_EabNQDwZEeO4Rq2NI2kHpA">
+ <parameterSubstitution xmi:id="_C7iaETztEeOo3uNUxreuKg" actual="_9OMcwDzsEeOo3uNUxreuKg" formal="_GcWowDwZEeO4Rq2NI2kHpA"/>
+ <parameterSubstitution xmi:id="_C7iaEjztEeOo3uNUxreuKg" actual="_9OMcwjzsEeOo3uNUxreuKg" formal="_LI3bMDwZEeO4Rq2NI2kHpA"/>
+ <parameterSubstitution xmi:id="_Ezb9kDzyEeOkQ_NYnvAgYw" actual="_Ezb9kTzyEeOkQ_NYnvAgYw" formal="_BJFoMDzxEeOkQ_NYnvAgYw">
+ <ownedActual xmi:type="uml:LiteralString" xmi:id="_Ezb9kTzyEeOkQ_NYnvAgYw" value="Hardware"/>
+ </parameterSubstitution>
+ <parameterSubstitution xmi:id="_Ezb9kjzyEeOkQ_NYnvAgYw" actual="_Ezb9kzzyEeOkQ_NYnvAgYw" formal="_VqNi4DzxEeOkQ_NYnvAgYw">
+ <ownedActual xmi:type="uml:LiteralString" xmi:id="_Ezb9kzzyEeOkQ_NYnvAgYw" value="Hardware"/>
+ </parameterSubstitution>
+ </templateBinding>
+ <packagedElement xmi:type="uml:Class" xmi:id="_vaUdNDzsEeOo3uNUxreuKg" name="SoftwarePackage">
+ <ownedAttribute xmi:id="_vaUdNTzsEeOo3uNUxreuKg" name="id">
+ <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/>
+ </ownedAttribute>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_vaUdNjzsEeOo3uNUxreuKg" name="SoftwareSpec">
+ <ownedAttribute xmi:id="_vaUdNzzsEeOo3uNUxreuKg" name="name">
+ <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+ </ownedAttribute>
+ <ownedAttribute xmi:id="_vaUdODzsEeOo3uNUxreuKg" name="version">
+ <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+ </ownedAttribute>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_9OMcwDzsEeOo3uNUxreuKg" name="HardwareComponent">
+ <ownedAttribute xmi:id="_9OMcwTzsEeOo3uNUxreuKg" name="id">
+ <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#Integer"/>
+ </ownedAttribute>
+ </packagedElement>
+ <packagedElement xmi:type="uml:Class" xmi:id="_9OMcwjzsEeOo3uNUxreuKg" name="HardwareSpec">
+ <ownedAttribute xmi:id="_9OMcwzzsEeOo3uNUxreuKg" name="deviceName">
+ <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+ </ownedAttribute>
+ <ownedAttribute xmi:id="_9OMcxDzsEeOo3uNUxreuKg" name="vendorName">
+ <type xmi:type="uml:PrimitiveType" href="pathmap://UML_LIBRARIES/UMLPrimitiveTypes.library.uml#String"/>
+ </ownedAttribute>
+ </packagedElement>
+ </packagedElement>
+ <profileApplication xmi:id="_1-baoD3BEeOmXIxoEtuCrA">
+ <eAnnotations xmi:id="_1-cowD3BEeOmXIxoEtuCrA" source="http://www.eclipse.org/uml2/2.0.0/UML">
+ <references xmi:type="ecore:EPackage" href="Bug180744.profile.uml#_zzE9YT3BEeOmXIxoEtuCrA"/>
+ </eAnnotations>
+ <appliedProfile href="Bug180744.profile.uml#_SZzrkD3BEeOmXIxoEtuCrA"/>
+ </profileApplication>
+ </uml:Package>
+ <bug180744profile:Limited xmi:id="_5RV84D3BEeOmXIxoEtuCrA" base_Classifier="_Fnn2oDwNEeO4Rq2NI2kHpA"/>
+</xmi:XMI>
diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744Test.java b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744Test.java
new file mode 100644
index 000000000..2ba9a4699
--- /dev/null
+++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/Bug180744Test.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2013 CEA 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 (CEA) - Initial API and implementation
+ *
+ */
+package org.eclipse.uml2.uml.bug.tests;
+
+import java.net.URL;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.eclipse.emf.common.util.ECollections;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
+import org.eclipse.uml2.common.util.UML2Util;
+import org.eclipse.uml2.uml.Association;
+import org.eclipse.uml2.uml.Class;
+import org.eclipse.uml2.uml.Enumeration;
+import org.eclipse.uml2.uml.EnumerationLiteral;
+import org.eclipse.uml2.uml.InstanceValue;
+import org.eclipse.uml2.uml.Operation;
+import org.eclipse.uml2.uml.Package;
+import org.eclipse.uml2.uml.Property;
+import org.eclipse.uml2.uml.TemplateableElement;
+import org.eclipse.uml2.uml.Type;
+import org.eclipse.uml2.uml.UMLPackage;
+import org.eclipse.uml2.uml.tests.util.StandaloneSupport;
+import org.eclipse.uml2.uml.util.UMLUtil;
+
+/**
+ * Tests the template expander utility.
+ *
+ * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=180744
+ */
+public class Bug180744Test
+ extends TestCase {
+
+ private ResourceSet rset;
+
+ private Package fixture;
+
+ public Bug180744Test() {
+ super();
+ }
+
+ public Bug180744Test(String name) {
+ super(name);
+ }
+
+ public static Test suite() {
+ return new TestSuite(Bug180744Test.class, "Bug 180744 tests"); //$NON-NLS-1$
+ }
+
+ /**
+ * Verify a classifier with a single template binding having all parameters
+ * explicitly substituted.
+ */
+ public void testClassifierSingleBindingExplicit() {
+ Class listOfBars = expand(getClass("ListOfBars"));
+
+ assertCollectionKind(listOfBars, "sequence");
+ assertOperation(listOfBars, "add", "element", getClass("Bar"));
+ }
+
+ /**
+ * Verify a classifier with a single template binding having a defaulted
+ * parameter.
+ */
+ public void testClassifierSingleBindingWithDefault() {
+ Class bagOfFoos = expand(getClass("BagOfFoos"));
+
+ assertCollectionKind(bagOfFoos, "bag"); // default enum literal sub
+ assertOperation(bagOfFoos, "remove", "element", getClass("Foo"));
+ }
+
+ /**
+ * Verify a classifier that has multiple bindings (to the same template, no
+ * less!).
+ */
+ public void testClassifierMultipleBindings() {
+ Class bagListOfFooBars = expand(getClass("BagListOfFooBars"));
+
+ // multiple bindings generate an anonymous superclass each, so these
+ // features are not owned but inherited
+
+ assertTrue(bagListOfFooBars.getOwnedAttributes().isEmpty());
+ assertTrue(bagListOfFooBars.getOwnedOperations().isEmpty());
+ assertEquals(2, bagListOfFooBars.getGeneralizations().size());
+
+ Class general1 = (Class) bagListOfFooBars.getGeneralizations().get(0)
+ .getGeneral();
+ Class general2 = (Class) bagListOfFooBars.getGeneralizations().get(1)
+ .getGeneral();
+
+ assertCollectionKind(general1, "sequence");
+ assertOperation(general1, "add", "element", getClass("Bar"));
+
+ assertCollectionKind(general2, "bag"); // default enum literal sub
+ assertOperation(general2, "remove", "element", getClass("Foo"));
+ }
+
+ /**
+ * Verify a package that has a single binding with actual parameters that
+ * are owned classes and defaulted name-expression parameters.
+ */
+ public void testPackageSingleBindingWithOwnedActualParametersAndDefaultNameParts() {
+ Package rentalSystem = expand(getPackage("rental system"));
+
+ Class car = getClass(rentalSystem, "Car");
+ Class carSpec = getClass(rentalSystem, "CarSpec");
+
+ assertProperty(car, "kind", carSpec);
+ assertProperty(car, "mileage", "Integer"); // not from the template
+
+ assertProperty(carSpec, "resource", car);
+ assertProperty(carSpec, "model", "String"); // not from the template
+
+ // the default name-expression part is an empty string replacing
+ // the '${ResourceKind}' part in '${ResourceKind}Allocation' and
+ // '${ResourceKind}Request'
+ Class allocation = getClass(rentalSystem, "Allocation");
+ Class request = getClass(rentalSystem, "Request");
+
+ assertProperty(allocation, "resource", car);
+ assertProperty(request, "kind", carSpec);
+
+ // the anonymous associations are not all merged together
+ assertAssociation(rentalSystem, car, carSpec);
+ assertAssociation(rentalSystem, allocation, car);
+ assertAssociation(rentalSystem, request, carSpec);
+ }
+
+ /**
+ * Verify a package that has multiple bindings to the same template, using
+ * name expressions to ensure that classes substituting for template
+ * parameters are expanded with distinct names so that they are not merged
+ * together.
+ */
+ public void testPackageMultipleBindingWithNameExpressionsHavingDistinctSubstitutions() {
+ Package deploymentSystem = expand(getPackage("deployment system"));
+
+ Class softwarePackage = getClass(deploymentSystem, "SoftwarePackage");
+ Class softwareSpec = getClass(deploymentSystem, "SoftwareSpec");
+
+ assertProperty(softwarePackage, "kind", softwareSpec);
+ assertProperty(softwarePackage, "id", "Integer"); // not from the
+ // template
+
+ assertProperty(softwareSpec, "resource", softwarePackage);
+ assertProperty(softwareSpec, "version", "String"); // not from the
+ // template
+
+ // the actual name-expression part is 'Software' replacing
+ // the '${ResourceKind}' part in '${ResourceKind}Allocation' and
+ // '${ResourceKind}Request'
+ Class softwareAllocation = getClass(deploymentSystem,
+ "SoftwareAllocation");
+ Class softwareRequest = getClass(deploymentSystem, "SoftwareRequest");
+
+ assertProperty(softwareAllocation, "resource", softwarePackage);
+ assertProperty(softwareRequest, "kind", softwareSpec);
+
+ // the anonymous associations are not all merged together
+ assertAssociation(deploymentSystem, softwarePackage, softwareSpec);
+ assertAssociation(deploymentSystem, softwareAllocation, softwarePackage);
+ assertAssociation(deploymentSystem, softwareRequest, softwareSpec);
+
+ Class hardwareComponent = getClass(deploymentSystem,
+ "HardwareComponent");
+ Class hardwareSpec = getClass(deploymentSystem, "HardwareSpec");
+
+ assertProperty(hardwareComponent, "kind", hardwareSpec);
+ assertProperty(hardwareComponent, "id", "Integer"); // not from the
+ // template
+
+ assertProperty(hardwareSpec, "resource", hardwareComponent);
+ assertProperty(hardwareSpec, "deviceName", "String"); // not from the
+ // template
+
+ // the actual name-expression part is 'Hardware' replacing
+ // the '${ResourceKind}' part in '${ResourceKind}Allocation' and
+ // '${ResourceKind}Request'
+ Class hardwareAllocation = getClass(deploymentSystem,
+ "HardwareAllocation");
+ Class hardwareRequest = getClass(deploymentSystem, "HardwareRequest");
+
+ assertProperty(hardwareAllocation, "resource", hardwareComponent);
+ assertProperty(hardwareRequest, "kind", hardwareSpec);
+
+ // the anonymous associations are not all merged together
+ assertAssociation(deploymentSystem, hardwareComponent, hardwareSpec);
+ assertAssociation(deploymentSystem, hardwareAllocation,
+ hardwareComponent);
+ assertAssociation(deploymentSystem, hardwareRequest, hardwareSpec);
+ }
+
+ /**
+ * Verifies that stereotype applications are copied from templates to the
+ * bound elements.
+ */
+ public void testStereotypesOnTemplates() {
+ Package rentalSystem = getPackage("rental system");
+
+ Class originalCar = getClass(rentalSystem, "Car");
+ assertEquals(0, originalCar.getAppliedStereotypes().size());
+
+ expand(rentalSystem);
+
+ Class car = getClass(rentalSystem, "Car");
+
+ assertSame(originalCar, car); // the class hasn't been replaced
+
+ assertEquals(1, car.getAppliedStereotypes().size());
+ assertEquals("Limited", car.getAppliedStereotypes().get(0).getName());
+ }
+
+ //
+ // Test framework
+ //
+
+ @Override
+ protected void setUp()
+ throws Exception {
+
+ rset = new ResourceSetImpl();
+ if (StandaloneSupport.isStandalone()) {
+ StandaloneSupport.init(rset);
+ }
+
+ fixture = getTestModel();
+ }
+
+ @Override
+ protected void tearDown()
+ throws Exception {
+
+ fixture = null;
+
+ // clean up the CacheAdapter as well as we can
+ for (Resource next : rset.getResources()) {
+ next.unload();
+ next.eAdapters().clear();
+ }
+
+ rset.getResources().clear();
+ rset.eAdapters().clear();
+ }
+
+ Class getClass(String name) {
+ return getClass(fixture, name);
+ }
+
+ Class getClass(Package package_, String name) {
+ return (Class) package_.getOwnedType(name);
+ }
+
+ Package getPackage(String name) {
+ return fixture.getNestedPackage(name);
+ }
+
+ Package getTestModel() {
+ URL url = getClass().getResource("Bug180744.uml"); //$NON-NLS-1$
+ return (Package) UML2Util.load(rset,
+ URI.createURI(url.toExternalForm()), UMLPackage.Literals.PACKAGE);
+ }
+
+ <T extends TemplateableElement> T expand(T boundElement) {
+ assertFalse(boundElement.getTemplateBindings().isEmpty());
+
+ UMLUtil.expand(boundElement, null);
+
+ assertExpanded(boundElement);
+
+ return boundElement;
+ }
+
+ void assertExpanded(TemplateableElement element) {
+ assertEquals(0, element.getTemplateBindings().size());
+ }
+
+ void assertCollectionKind(Class collectionType, String kindLiteral) {
+ Enumeration kindEnum = (Enumeration) fixture
+ .getOwnedType("CollectionKind");
+ EnumerationLiteral literal = kindEnum.getOwnedLiteral(kindLiteral);
+ assertNotNull(literal);
+
+ Property kind = collectionType.getOwnedAttribute("kind", kindEnum);
+ assertNotNull(kind);
+ assertTrue(kind.isStatic());
+ assertTrue(kind.getDefaultValue() instanceof InstanceValue);
+ assertSame(literal,
+ ((InstanceValue) kind.getDefaultValue()).getInstance());
+ }
+
+ void assertProperty(Class class_, String name, Type type) {
+ Property property = class_.getOwnedAttribute(name, type);
+ assertNotNull(property);
+ }
+
+ void assertProperty(Class class_, String name, String type) {
+ Property property = class_.getOwnedAttribute(name, null);
+ assertNotNull(property);
+ assertNotNull(property.getType());
+ assertEquals(type, property.getType().getName());
+ }
+
+ void assertOperation(Class class_, String name, String param1Name,
+ Type param1Type) {
+ Operation operation = class_.getOwnedOperation(name,
+ ECollections.singletonEList(param1Name),
+ ECollections.singletonEList(param1Type));
+ assertNotNull(operation);
+ }
+
+ void assertAssociation(Package package_, Type endType1, Type endType2) {
+ Association found = null;
+
+ for (Type next : package_.getOwnedTypes()) {
+ if (next instanceof Association) {
+ Association association = (Association) next;
+
+ if (association.isBinary()
+ && association.getEndTypes().contains(endType1)
+ && association.getEndTypes().contains(endType2)) {
+
+ found = association;
+ break;
+ }
+ }
+ }
+
+ assertNotNull(found);
+ }
+}
diff --git a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/UMLBugTests.java b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/UMLBugTests.java
index 4a96e1585..3fd1096a3 100644
--- a/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/UMLBugTests.java
+++ b/tests/org.eclipse.uml2.uml.tests/src/org/eclipse/uml2/uml/bug/tests/UMLBugTests.java
@@ -7,7 +7,7 @@
*
* Contributors:
* Christian W. Damus (CEA) - initial API and implementation
- * Christian W. Damus (CEA) - 409396, 403365, 300957, 405061, 401682, 176998
+ * Christian W. Damus (CEA) - 409396, 403365, 300957, 405061, 401682, 176998, 180744
*
*/
package org.eclipse.uml2.uml.bug.tests;
@@ -46,6 +46,7 @@ public class UMLBugTests
"Bug 401682 tests", Bug401682Test.class)); //$NON-NLS-1$
result.addTest(Bug409396Test.suite());
result.addTest(Bug176998Test.suite());
+ result.addTest(Bug180744Test.suite());
return result;
}

Back to the top