Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java')
-rw-r--r--plugins/org.eclipse.uml2.uml/src/org/eclipse/uml2/uml/util/UMLUtil.java888
1 files changed, 812 insertions, 76 deletions
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);
}
/**

Back to the top