/*****************************************************************************
* Copyright (c) 2010 CEA LIST.
*
* 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:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.uml.tools.utils;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.papyrus.infra.core.utils.PapyrusEcoreUtils;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.ConnectableElement;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.Message;
import org.eclipse.uml2.uml.MessageEvent;
import org.eclipse.uml2.uml.MessageOccurrenceSpecification;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLPackage;
/**
* A Helper class for UML
*
* @author Camille Letavernier
*/
//TODO/FIXME : Check implementations. Most of them are old and don't always match the
//specification for some cases.
public class UMLUtil {
/**
* Retrieve the UML semantic element from the given Object.
* This method relies on {@link EMFHelper#getEObject(Object)} to resolve
* an EObject from an Object, then checks if the resulting EObject is a
* UML Element.
*
* @param source
* The Object to resolve
* @return
* The UML semantic element, or null if it couldn't be resolved
*/
public static Element resolveUMLElement(Object source) {
EObject eElement = EMFHelper.getEObject(source);
if(eElement instanceof Element) {
return (Element)eElement;
}
return null;
}
/**
* Tests if a class is a subclass of another class. The classes are described
* by their className, in the UML Metamodel.
*
* @param className
* @param superclassName
* @return
* True if the class className is a subclass of the class superclassName
*/
public static boolean isSubClass(String className, String superclassName) {
EClass eClass = (EClass)getUMLMetamodel().getEClassifier(className);
EClass superClass = (EClass)getUMLMetamodel().getEClassifier(superclassName);
return EMFHelper.isSubclass(eClass, superClass);
}
/**
* Retrieve the EditingDomain for the given source object
*
* @param source
* @return
* The source object's editing domain, or null if it couldn't be found
*/
public static EditingDomain resolveEditingDomain(Object source) {
return EMFHelper.resolveEditingDomain(resolveUMLElement(source));
}
/**
* @return the UML EPackage
*/
public static EPackage getUMLMetamodel() {
return UMLPackage.eINSTANCE;
}
/**
* Search the given stereotype (By name) on the given UML Element.
* If the search is not strict, the name may be the qualified name of a
* sub-stereotype of an applied stereotype
*
* @param umlElement
* The UML Element on which the stereotype is applied
* @param stereotypeName
* The qualified name of the stereotype
* @param strict
* If set to true, only a stereotype matching the exact qualified name
* will be returned. Otherwise, any subtype of the given stereotype may be
* returned. Note that if more than one stereotype is a subtype of the
* given stereotype, the first matching stereotype is returned.
* @return
* The first matching stereotype, or null if none was found
*/
public static Stereotype getAppliedStereotype(Element umlElement, String stereotypeName, boolean strict) {
Stereotype stereotype = umlElement.getAppliedStereotype(stereotypeName);
if(strict || stereotype != null) {
return stereotype;
}
//The parent stereotype is not always applicable...
//stereotype = umlElement.getApplicableStereotype(stereotypeName);
List<Stereotype> subStereotypes = findSubstereotypes(umlElement, stereotypeName);
for(Stereotype subStereotype : subStereotypes) {
if(umlElement.getAppliedStereotypes().contains(subStereotype)) {
return subStereotype;
}
}
return null;
}
/**
* Finds the Stereotype matching the given name.
* The search is done in the context of the given UML Element
* (i.e. the Profiles applied on the Element's nearest package)
*
* @param umlElement
* @param stereotypeName
* @return
*/
public static Stereotype findStereotype(Element umlElement, String stereotypeName) {
Stereotype stereotype = null;
org.eclipse.uml2.uml.Package umlPackage = umlElement.getNearestPackage();
if(umlPackage == null) {
stereotype = umlElement.getApplicableStereotype(stereotypeName);
} else {
outerLoop: for(Profile profile : umlPackage.getAllAppliedProfiles()) {
for(Stereotype ownedStereotype : profile.getOwnedStereotypes()) {
if(ownedStereotype.getQualifiedName().equals(stereotypeName)) {
stereotype = ownedStereotype;
break outerLoop;
}
}
}
}
return stereotype;
}
/**
* Returns all stereotypes matching the given qualified stereotype name, and their substereotypes
* The search is performed in the context of the given UML Element, i.e. the profiles applied
* on the Element's nearest package
*
* @param umlElement
* @param stereotypeName
* @return
*/
public static List<Stereotype> findSubstereotypes(Element umlElement, String stereotypeName) {
Set<Stereotype> stereotypes = new HashSet<Stereotype>();
org.eclipse.uml2.uml.Package umlPackage = umlElement.getNearestPackage();
if(umlPackage == null) {
Stereotype stereotype = umlElement.getApplicableStereotype(stereotypeName);
if(stereotype != null) {
stereotypes.add(stereotype);
}
} else {
for(Profile profile : umlPackage.getAllAppliedProfiles()) {
for(Stereotype ownedStereotype : profile.getOwnedStereotypes()) {
for(Stereotype superStereotype : getAllSuperStereotypes(ownedStereotype)) {
if(superStereotype.getQualifiedName().equals(stereotypeName)) {
stereotypes.add(ownedStereotype);
}
}
}
}
}
return new LinkedList<Stereotype>(stereotypes);
}
/**
* Returns a collection of all super stereotypes of the given stereotype
* (Including itself)
*
* @param stereotype
* @return
* A collection of all super stereotypes
*/
public static Collection<Stereotype> getAllSuperStereotypes(Stereotype stereotype) {
Set<Stereotype> result = new HashSet<Stereotype>();
if(stereotype != null) {
getAllSuperStereotypes(stereotype, result);
}
return result;
}
private static void getAllSuperStereotypes(Stereotype stereotype, Set<Stereotype> result) {
result.add(stereotype);
for(Classifier superClassifier : stereotype.getGenerals()) {
if(superClassifier instanceof Stereotype && !result.contains(superClassifier)) {
getAllSuperStereotypes((Stereotype)superClassifier, result);
}
}
}
/**
* Retrieves the UML Class associated to the given Message
*
* @param message
* @return the UML Class associated to the given Message
*/
public static org.eclipse.uml2.uml.Class getContextClassForMessage(Message message) {
MessageOccurrenceSpecification receiveEvent = (MessageOccurrenceSpecification)message.getReceiveEvent();
if(receiveEvent == null) {
return null;
}
return getContextClassForMessageOccurrence(receiveEvent);
}
/**
* Retrieves the UML Class associated to the given MessageOccurrenceSpecification
*
* @param messageOccurrence
* @return the UML Class associated to the given MessageOccurrenceSpecification
*/
public static org.eclipse.uml2.uml.Class getContextClassForMessageOccurrence(MessageOccurrenceSpecification messageOccurrence) {
List<Lifeline> lifelines = messageOccurrence.getCovereds();
if(lifelines.isEmpty()) {
return null; //We can't find the context
} else if(lifelines.size() == 1) {
Lifeline lifeline = lifelines.get(0);
ConnectableElement element = lifeline.getRepresents();
if(element == null) {
return null;
}
Type type = element.getType();
if(type instanceof org.eclipse.uml2.uml.Class) {
org.eclipse.uml2.uml.Class clazz = (org.eclipse.uml2.uml.Class)type;
return clazz;
} else {
return null; //The type is not a Class
}
} else {
return null; //Too many contexts : which one should we choose ?
}
}
/**
* Finds the UML Class associated to the given MessageEvent
*
* @param event
* @return the Class associated to the given MessageEvent
*/
public static Class getContextClassForMessageEvent(MessageEvent event) {
Collection<EStructuralFeature.Setting> settings = PapyrusEcoreUtils.getUsages(event);
if(settings.isEmpty()) {
return null;
}
if(settings.size() == 1) {
EObject referer = settings.iterator().next().getEObject();
if(referer instanceof MessageOccurrenceSpecification) {
return UMLUtil.getContextClassForMessageOccurrence((MessageOccurrenceSpecification)referer);
} else {
return null;
}
}
MessageOccurrenceSpecification referer = null;
EObject newReferer = null;
for(EStructuralFeature.Setting setting : settings) {
newReferer = setting.getEObject();
if(!(newReferer instanceof MessageOccurrenceSpecification)) {
continue;
}
if(referer == null || referer == newReferer) {
referer = (MessageOccurrenceSpecification)newReferer;
} else {
referer = null;
break;
}
}
if(referer == null) {
return null;
}
return UMLUtil.getContextClassForMessageOccurrence(referer);
}
}