blob: b95a37ac2e856cb7f84b84ef7bafda0c0f39d5a6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2016 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.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.internal.utilities.PivotObjectImpl;
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 caseClassifier(org.eclipse.uml2.uml.Classifier umlClassifier) {
assert umlClassifier != null;
org.eclipse.ocl.pivot.Class asClass = converter.getCreated(org.eclipse.ocl.pivot.Class.class, umlClassifier);
List<org.eclipse.ocl.pivot.Class> asSuperClasses = new ArrayList<org.eclipse.ocl.pivot.Class>();
if (asClass != null) {
for (org.eclipse.uml2.uml.Generalization umlGeneralization : umlClassifier.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(asClass.getSuperClasses(), asSuperClasses);
}
return asClass;
}
@Override
public Object caseDataType(org.eclipse.uml2.uml.DataType umlDataType) {
return super.caseClassifier(umlDataType); // Redundant override needed for API compatibility
}
@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);
((PivotObjectImpl)asThis2ThatProperty).setESObject(umlThis2ThatProperty);
converter.addProperty(asThisClass, asThis2ThatProperty);
asAssociationClassProperties.put(umlThis2ThatProperty, umlThat2ThisProperty, asThis2ThatProperty);
//
asThat2ThisProperty.setIsRequired(getEndIsRequired(umlThat2ThisProperty));
asThat2ThisProperty.setIsImplicit(umlThat2ThisProperty.getOwningAssociation() != null);
asThat2ThisProperty.setOpposite(asThis2ThatProperty);
((PivotObjectImpl)asThat2ThisProperty).setESObject(umlThat2ThisProperty);
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;
}
}