/******************************************************************************* * Copyright (c) 2011, 2015 Willink Transformations 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: * E.D.Willink - initial API and implementation * E.D.Willink (CEA LIST) - Bug 400744 *******************************************************************************/ package org.eclipse.ocl.pivot.uml.internal.es2as; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.ocl.pivot.AssociationClass; import org.eclipse.ocl.pivot.DataType; import org.eclipse.ocl.pivot.Element; import org.eclipse.ocl.pivot.Profile; import org.eclipse.ocl.pivot.ProfileApplication; import org.eclipse.ocl.pivot.Property; import org.eclipse.ocl.pivot.Slot; import org.eclipse.ocl.pivot.Stereotype; import org.eclipse.ocl.pivot.StereotypeExtender; import org.eclipse.ocl.pivot.Type; import org.eclipse.ocl.pivot.TypedElement; import org.eclipse.ocl.pivot.internal.complete.StandardLibraryInternal; import org.eclipse.ocl.pivot.internal.manager.PivotMetamodelManager; import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal; import org.eclipse.ocl.pivot.internal.utilities.External2AS; import org.eclipse.ocl.pivot.utilities.ClassUtil; import org.eclipse.ocl.pivot.utilities.PivotUtil; import org.eclipse.ocl.pivot.utilities.ValueUtil; import org.eclipse.uml2.uml.util.UMLSwitch; // // Originally everything was in the Reference pass but the Stereotype resolution preceded it and got steadily more complicated // so all activities were moved to a new last Use pass. Simple reference resolving activities can be moved from the Use pass to the Reference pass. // public class UML2ASReferenceSwitch extends UMLSwitch<Object> { private static final Logger logger = Logger.getLogger(UML2ASReferenceSwitch.class); protected final @NonNull UML2AS converter; protected final @NonNull EnvironmentFactoryInternal environmentFactory; protected final @NonNull StandardLibraryInternal standardLibrary; private Set<EClass> doneWarnings = null; public UML2ASReferenceSwitch(@NonNull UML2AS converter) { this.converter = converter; this.environmentFactory = converter.getEnvironmentFactory(); this.standardLibrary = converter.getStandardLibrary(); } @Override public Object caseAssociation(org.eclipse.uml2.uml.Association umlAssociation) { assert umlAssociation != null; AssociationClass asAssociationClass = createAssociationClassProperties(umlAssociation); List<org.eclipse.ocl.pivot.Class> asSuperClasses = asAssociationClass.getSuperClasses(); if (asSuperClasses.isEmpty()) { asSuperClasses.add(standardLibrary.getOclElementType()); } return asAssociationClass; } @Override public Object caseAssociationClass(org.eclipse.uml2.uml.AssociationClass umlAssociationClass) { assert umlAssociationClass != null; createAssociationClassProperties(umlAssociationClass); return caseClass(umlAssociationClass); } @Override public org.eclipse.ocl.pivot.Class caseClass(org.eclipse.uml2.uml.Class umlClass) { assert umlClass != null; org.eclipse.ocl.pivot.Class asClass = converter.getCreated(org.eclipse.ocl.pivot.Class.class, umlClass); if (asClass != null) { List<org.eclipse.ocl.pivot.@NonNull Class> asSuperClasses = ClassUtil.nullFree(asClass.getSuperClasses()); doSwitchAll(org.eclipse.ocl.pivot.Class.class, asSuperClasses, umlClass.getSuperClasses()); if (asSuperClasses.isEmpty()) { asSuperClasses.add(standardLibrary.getOclElementType()); } } return asClass; } @Override public Object caseDataType(org.eclipse.uml2.uml.DataType umlDataType) { assert umlDataType != null; DataType pivotElement = converter.getCreated(DataType.class, umlDataType); List<org.eclipse.ocl.pivot.Class> asSuperClasses = new ArrayList<org.eclipse.ocl.pivot.Class>(); if (pivotElement != null) { for (org.eclipse.uml2.uml.Generalization umlGeneralization : umlDataType.getGeneralizations()) { org.eclipse.uml2.uml.Classifier umlGeneral = umlGeneralization.getGeneral(); if (umlGeneral != null) { org.eclipse.ocl.pivot.Class asGeneral = converter.getCreated(org.eclipse.ocl.pivot.Class.class, umlGeneral); if ((asGeneral != null) && !asSuperClasses.contains(asGeneral)) { asSuperClasses.add(asGeneral); } } } if (asSuperClasses.isEmpty()) { org.eclipse.ocl.pivot.Class oclElementType = standardLibrary.getOclElementType(); asSuperClasses.add(oclElementType); } converter.refreshList(pivotElement.getSuperClasses(), asSuperClasses); } return pivotElement; } @Override public Object caseExtension(org.eclipse.uml2.uml.Extension umlExtension) { assert umlExtension != null; List<org.eclipse.uml2.uml.Property> memberEnds = umlExtension.getMemberEnds(); // FIXME re-implement using/emulating createAssociationClassProperties if (memberEnds.size() == 2) { org.eclipse.uml2.uml.Property firstEnd = memberEnds.get(0); org.eclipse.uml2.uml.Property secondEnd = memberEnds.get(1); if ((firstEnd != null) && (secondEnd != null)) { Property firstProperty = converter.getCreated(Property.class, firstEnd); Property secondProperty = converter.getCreated(Property.class, secondEnd); if ((firstProperty != null) && (secondProperty != null)) { firstProperty.setOpposite(secondProperty); secondProperty.setOpposite(firstProperty); } } } StereotypeExtender asTypeExtension = converter.getCreated(StereotypeExtender.class, umlExtension); if (asTypeExtension != null) { org.eclipse.uml2.uml.Class umlMetaclass = umlExtension.getMetaclass(); org.eclipse.uml2.uml.Stereotype umlStereotype = umlExtension.getStereotype(); if ((umlMetaclass != null) && (umlStereotype != null)) { org.eclipse.ocl.pivot.Class asMetaclass = converter.getCreated(org.eclipse.ocl.pivot.Class.class, umlMetaclass); Stereotype asStereotype = converter.getCreated(Stereotype.class, umlStereotype); if ((asMetaclass != null) && (asStereotype != null)) { asTypeExtension.setOwningStereotype(asStereotype); asTypeExtension.setClass(asMetaclass); if (UML2AS.ADD_TYPE_EXTENSION.isActive()) { UML2AS.ADD_TYPE_EXTENSION.println(asTypeExtension.toString()); } converter.addTypeExtension(asTypeExtension); } } } return this; } @Override public Object caseProfileApplication(org.eclipse.uml2.uml.ProfileApplication umlProfileApplication) { assert umlProfileApplication != null; ProfileApplication asProfileApplication = converter.getCreated(ProfileApplication.class, umlProfileApplication); if (asProfileApplication != null) { org.eclipse.uml2.uml.Profile umlProfile = umlProfileApplication.getAppliedProfile(); if (umlProfile != null) { Profile asProfile = converter.getCreated(Profile.class, umlProfile); asProfileApplication.setAppliedProfile(asProfile); converter.addProfileApplication(asProfileApplication); } } return this; } @Override public Object caseProperty(org.eclipse.uml2.uml.Property umlProperty) { assert umlProperty != null; org.eclipse.uml2.uml.Association umlAssociation = umlProperty.getAssociation(); assert (umlAssociation == null) || (umlAssociation instanceof org.eclipse.uml2.uml.Extension); caseTypedElement(umlProperty); Property asProperty = converter.getCreated(Property.class, umlProperty); if (asProperty != null) { if (asProperty.getName() == null) { org.eclipse.uml2.uml.Type umlTargetType = umlProperty.getType(); if (umlTargetType != null) { Type asTargetType = converter.getCreated(Type.class, umlTargetType); if (asTargetType != null) { asProperty.setName(asTargetType.getName()); } } } org.eclipse.ocl.pivot.Class pivotType = null; if (umlAssociation != null) { if (umlProperty.getOwningAssociation() != null) { asProperty.setIsImplicit(true); } org.eclipse.uml2.uml.Property opposite = getOtherEnd(umlProperty); if (opposite != null) { org.eclipse.uml2.uml.Type oppositeType = opposite.getType(); if (oppositeType != null) { pivotType = converter.getCreated(org.eclipse.ocl.pivot.Class.class, oppositeType); } } } if (pivotType == null) { EObject eContainer = umlProperty.eContainer(); if (eContainer != null) { pivotType = converter.getCreated(org.eclipse.ocl.pivot.Class.class, eContainer); } } if (pivotType != null) { converter.addProperty(pivotType, asProperty); } else { // System.err.println("Failed to find parent for " + umlProperty); } } return asProperty; } @Override public Object caseSlot(org.eclipse.uml2.uml.Slot umlSlot) { assert umlSlot != null; Slot asSlot = converter.getCreated(Slot.class, umlSlot); if (asSlot != null) { org.eclipse.uml2.uml.StructuralFeature umlDefiningFeature = umlSlot.getDefiningFeature(); Property asProperty = umlDefiningFeature != null ? converter.getCreated(Property.class, umlDefiningFeature) : null; asSlot.setDefiningProperty(asProperty); } return this; } @Override public Object caseStereotype(org.eclipse.uml2.uml.Stereotype umlStereotype) { assert umlStereotype != null; // caseClass(umlStereotype); Stereotype asStereotype = converter.getCreated(Stereotype.class, umlStereotype); if (asStereotype != null) { List<org.eclipse.ocl.pivot.@NonNull Class> asSuperClasses = ClassUtil.nullFree(asStereotype.getSuperClasses()); doSwitchAll(org.eclipse.ocl.pivot.Class.class, asSuperClasses, umlStereotype.getSuperClasses()); org.eclipse.ocl.pivot.Class oclStereotype = standardLibrary.getOclStereotypeType(); if (!asSuperClasses.contains(oclStereotype)) { asSuperClasses.add(oclStereotype); } converter.addStereotype(asStereotype); } return asStereotype; } @Override public EObject caseTypedElement(org.eclipse.uml2.uml.TypedElement umlTypedElement) { assert umlTypedElement != null; TypedElement pivotElement = converter.getCreated(TypedElement.class, umlTypedElement); if (pivotElement != null) { converter.resolveMultiplicity(pivotElement, umlTypedElement); } return pivotElement; } private @NonNull AssociationClass createAssociationClassProperties(org.eclipse.uml2.uml.@NonNull Association umlAssociation) { // System.out.println("Association " + umlAssociation.getName() + ", " + NameUtil.debugSimpleName(umlAssociation) + " in " + NameUtil.debugSimpleName(converter.getCreatedMap()) + " ? "); AssociationClass asAssociationClass = converter.getCreated(AssociationClass.class, umlAssociation); assert asAssociationClass != null; List<org.eclipse.uml2.uml.@NonNull Property> umlMemberEnds = converter.getSafeMemberEnds(umlAssociation); AssociationClassProperties asAssociationClassProperties = new AssociationClassProperties(umlMemberEnds); String associationName = asAssociationClass.getName(); if (associationName != null) { // Null name suppresses navigation to Association; see BUG 413766 comments // // Create mutually opposite pairs of Property between each UML member end's referenced type and the AssociationClass. // for (int i = 0; i < umlMemberEnds.size(); i++) { org.eclipse.uml2.uml.Property umlMemberProperty = umlMemberEnds.get(i); org.eclipse.uml2.uml.Type umlMemberType = umlMemberProperty.getType(); if (umlMemberType != null) { org.eclipse.ocl.pivot.Class asMemberClass = converter.getCreated(org.eclipse.ocl.pivot.Class.class, umlMemberType); if (asMemberClass != null) { Type asAssociationClassEndType = getToAssociationEndType(asAssociationClass, umlMemberProperty, umlMemberEnds); Property asMember2AssociationProperty = PivotUtil.createProperty(associationName, asAssociationClassEndType); Property asAssociation2MemberProperty = PivotUtil.createProperty(getEndName(umlMemberProperty), asMemberClass); // asMember2AssociationProperty.setIsRequired(getToAssociationEndIsRequired(umlMemberProperty, umlMemberEnds)); asMember2AssociationProperty.setIsImplicit(!(umlAssociation instanceof AssociationClass)); asMember2AssociationProperty.setOpposite(asAssociation2MemberProperty); converter.addProperty(asAssociationClass, asAssociation2MemberProperty); asAssociationClassProperties.put(null, umlMemberProperty, asAssociation2MemberProperty); // asAssociation2MemberProperty.setIsRequired(true); asAssociation2MemberProperty.setIsImplicit(false); asAssociation2MemberProperty.setOpposite(asMember2AssociationProperty); converter.addProperty(asMemberClass, asMember2AssociationProperty); asAssociationClassProperties.put(umlMemberProperty, null, asMember2AssociationProperty); } } } } // // Create mutually opposite pairs of Property between the types referenced by each pair of UML member ends. // for (int iThis2That = 0; iThis2That < umlMemberEnds.size(); iThis2That++) { org.eclipse.uml2.uml.Property umlThis2ThatProperty = umlMemberEnds.get(iThis2That); org.eclipse.uml2.uml.Type umlThatType = umlThis2ThatProperty.getType(); if (umlThatType != null) { org.eclipse.ocl.pivot.Class asThatClass = converter.getCreated(org.eclipse.ocl.pivot.Class.class, umlThatType); if (asThatClass != null) { for (int iThat2This = iThis2That+1; iThat2This < umlMemberEnds.size(); iThat2This++) { org.eclipse.uml2.uml.Property umlThat2ThisProperty = umlMemberEnds.get(iThat2This); org.eclipse.uml2.uml.Type umlThisType = umlThat2ThisProperty.getType(); if (umlThisType != null) { org.eclipse.ocl.pivot.Class asThisClass = converter.getCreated(org.eclipse.ocl.pivot.Class.class, umlThisType); if (asThisClass != null) { Type asThatEndType = getInterMemberEndType(asThatClass, umlThis2ThatProperty, umlMemberEnds); Type asThisEndType = getInterMemberEndType(asThisClass, umlThat2ThisProperty, umlMemberEnds); Property asThis2ThatProperty = PivotUtil.createProperty(getEndName(umlThis2ThatProperty), asThatEndType); Property asThat2ThisProperty = PivotUtil.createProperty(getEndName(umlThat2ThisProperty), asThisEndType); // asThis2ThatProperty.setIsRequired(getEndIsRequired(umlThis2ThatProperty)); asThis2ThatProperty.setIsImplicit(umlThis2ThatProperty.getOwningAssociation() != null); asThis2ThatProperty.setOpposite(asThat2ThisProperty); converter.addProperty(asThisClass, asThis2ThatProperty); asAssociationClassProperties.put(umlThis2ThatProperty, umlThat2ThisProperty, asThis2ThatProperty); // asThat2ThisProperty.setIsRequired(getEndIsRequired(umlThat2ThisProperty)); asThat2ThisProperty.setIsImplicit(umlThat2ThisProperty.getOwningAssociation() != null); asThat2ThisProperty.setOpposite(asThis2ThatProperty); converter.addProperty(asThatClass, asThat2ThisProperty); asAssociationClassProperties.put(umlThat2ThisProperty, umlThis2ThatProperty, asThat2ThisProperty); } } } } } } converter.addAssociationClassProperties(asAssociationClass, asAssociationClassProperties); return asAssociationClass; } public Object doInPackageSwitch(EObject eObject) { int classifierID = eObject.eClass().getClassifierID(); return doSwitch(classifierID, eObject); } public <T extends Element> void doSwitchAll(@NonNull Class<T> pivotClass, /*@NonNull*/ Collection<T> pivotElements, /*@NonNull*/ List<? extends EObject> eObjects) { assert pivotElements != null; assert eObjects != null; for (EObject eObject : eObjects) { if (eObject != null) { @Nullable T pivotElement = converter.getCreated(pivotClass, eObject); if (pivotElement == null) { Resource eResource = eObject.eResource(); if (eResource != null) { External2AS adapter = UML2AS.findAdapter(eResource, environmentFactory); if (adapter != null) { pivotElement = adapter.getCreated(pivotClass, eObject); } } } if (pivotElement == null) { if (!(eObject instanceof org.eclipse.uml2.uml.Constraint)) { System.out.println("Reference switching " + eObject); } @SuppressWarnings("unchecked")T doSwitchResult = (T) doSwitch(eObject); pivotElement = doSwitchResult; } if (pivotElement != null) { pivotElements.add(pivotElement); } else { if (doneWarnings == null) { doneWarnings = new HashSet<EClass>(); } EClass eClass = eObject.eClass(); if (doneWarnings.add(eClass)) { logger.warn("Failed to create a pivot representation of a UML '" + eClass.getName() + "'"); } } } } } private boolean getEndIsRequired(org.eclipse.uml2.uml.@NonNull Property umlProperty) { return umlProperty.getLower() != 0; } private @NonNull String getEndName(org.eclipse.uml2.uml.@NonNull Property umlProperty) { String name = umlProperty.getName(); if (name != null) { return name; } else { return ClassUtil.nonNullState(umlProperty.getType().getName()); } } private @NonNull Type getInterMemberEndType(org.eclipse.ocl.pivot.@NonNull Class asClass, org.eclipse.uml2.uml.@NonNull Property umlProperty, @NonNull List<org.eclipse.uml2.uml.@NonNull Property> umlMemberEnds) { if (!umlProperty.isMultivalued()) { return asClass; } boolean isOrdered = umlProperty.isOrdered(); boolean isUnique = umlProperty.isUnique(); if (umlMemberEnds.size() > 2) { for (org.eclipse.uml2.uml.@NonNull Property umlOtherProperty : umlMemberEnds) { if (umlOtherProperty != umlProperty) { if (!umlOtherProperty.isOrdered()) { isOrdered = false; } } } } PivotMetamodelManager metamodelManager = environmentFactory.getMetamodelManager(); return metamodelManager.getCollectionType(isOrdered, isUnique, asClass, true, ValueUtil.integerValueOf(umlProperty.getLower()), null); } private boolean getToAssociationEndIsRequired(org.eclipse.uml2.uml.@NonNull Property umlProperty, @NonNull List<org.eclipse.uml2.uml.@NonNull Property> umlMemberEnds) { return umlProperty.getLower() != 0; // for (org.eclipse.uml2.uml.@NonNull Property umlOtherProperty : umlMemberEnds) { // if (umlOtherProperty != umlProperty) { // if (umlOtherProperty.getLower() != 0) { // return true; // } // } // } // return false; } private @NonNull Type getToAssociationEndType(org.eclipse.ocl.pivot.@NonNull Class asClass, org.eclipse.uml2.uml.@NonNull Property umlProperty, @NonNull List<org.eclipse.uml2.uml.@NonNull Property> umlMemberEnds) { if (!umlProperty.isMultivalued()) { return asClass; } boolean isMultivalued = false; boolean isOrdered = true; for (org.eclipse.uml2.uml.@NonNull Property umlOtherProperty : umlMemberEnds) { if (umlOtherProperty != umlProperty) { if (umlOtherProperty.isMultivalued()) { isMultivalued = true; } if (!umlOtherProperty.isOrdered()) { isOrdered = false; } } } if (!isMultivalued) { return asClass; } PivotMetamodelManager metamodelManager = environmentFactory.getMetamodelManager(); return metamodelManager.getCollectionType(isOrdered, true, asClass, true, ValueUtil.integerValueOf(umlProperty.getLower()), null); } protected org.eclipse.uml2.uml.Property getOtherEnd(org.eclipse.uml2.uml.@NonNull Property umlProperty) { org.eclipse.uml2.uml.Property otherEnd = umlProperty.getOtherEnd(); if (otherEnd != null) { return otherEnd; } // Workaround BUG 491587 whereby UML has three ends two of them duplicates with distinct Class/Association ownership. org.eclipse.uml2.uml.Association association = umlProperty.getAssociation(); if (association != null) { List<org.eclipse.uml2.uml.Property> memberEnds = new ArrayList<org.eclipse.uml2.uml.Property>(association.getMemberEnds()); memberEnds.remove(umlProperty); for (org.eclipse.uml2.uml.Property aProperty : memberEnds) { if (!aProperty.getName().equals(umlProperty)) { return aProperty; } } } return otherEnd; } public org.eclipse.uml2.uml.@Nullable Property getOtherEnd(@NonNull List<org.eclipse.uml2.uml.Property> umlMemberEnds, org.eclipse.uml2.uml.@NonNull Property umlProperty) { for (org.eclipse.uml2.uml.Property umlMemberEnd : umlMemberEnds) { if (umlMemberEnd != umlProperty) { return umlMemberEnd; } } return null; } }