/*****************************************************************************
* Copyright (c) 2012 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.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.UMLPackage;
/**
* A class containing static utility method regarding UML profiles
*
* @author Camille Letavernier
*
*/
public class ProfileUtil {
/**
* Checks if the profile applied has been changed since last application (definition does not match.
*
* @param _package
* on which the profile is applied
* @param _profile
* the applied profile
* @return true if the profile has changed
*/
public static boolean isDirty(Package _package, Profile _profile) {
boolean isDirty = false;
if(_profile == null || _profile.eResource() == null) {
return false;
}
// Retrieve model resourceSet
ResourceSet pkge_resourceSet = _package.eResource().getResourceSet();
if (pkge_resourceSet != null){
// Retrieve profile resource
URI prof_URI = _profile.eResource().getURI();
Resource modelResource = pkge_resourceSet.getResource(prof_URI, true);
if(modelResource != null && modelResource.getContents().get(0) instanceof Profile) {
// ckeck applied profile application definition vs profile definition referenced in file
Profile profileInFile = (Profile)(modelResource.getContents().get(0));
if(_package.getProfileApplication(_profile) != null) {
EPackage appliedProfileDefinition = _package.getProfileApplication(_profile).getAppliedDefinition();
EPackage fileProfileDefinition = null;
// Check profiles qualified names to ensure the correct profiles are compared
String appliedProfileName = _profile.getQualifiedName();
String fileProfileName = profileInFile.getQualifiedName();
if(!appliedProfileName.equals(fileProfileName)) {
// The profile must be a subprofile
Iterator<Profile> it = PackageUtil.getSubProfiles(profileInFile).iterator();
while(it.hasNext()) {
Profile current = it.next();
fileProfileName = current.getQualifiedName();
if(fileProfileName.equals(appliedProfileName)) {
profileInFile = current;
}
}
}
fileProfileDefinition = profileInFile.getDefinition();
if(appliedProfileDefinition != fileProfileDefinition) {
isDirty = true;
}
}
}
}
return isDirty;
}
public static List<Stereotype> findAllSubStereotypes(Stereotype parentStereotype, Package umlPackage, boolean concreteOnly) {
Collection<Stereotype> result = new LinkedHashSet<Stereotype>();
for(Profile profile : umlPackage.getAllAppliedProfiles()) {
List<Stereotype> allStereotypes = new LinkedList<Stereotype>();
findAllStereotypes(profile, allStereotypes);
for(Stereotype stereotype : allStereotypes) {
if(concreteOnly && stereotype.isAbstract()) {
continue; //Skip abstract stereotypes
}
if(isSubStereotype(parentStereotype, stereotype)) {
result.add(stereotype);
}
}
}
return new LinkedList<Stereotype>(result);
}
public static boolean isSubStereotype(Stereotype parentStereotype, Stereotype childStereotype) {
if(parentStereotype == childStereotype) {
return true;
}
return childStereotype.getGenerals().contains(parentStereotype); //TODO : This is probably not recursive...
}
/**
* Finds recursively all stereotypes contained in this profile
*
* @param profile
*
* @param result
*/
public static void findAllStereotypes(Profile profile, List<Stereotype> result) {
for(Stereotype stereotype : profile.getOwnedStereotypes()) {
result.add(stereotype);
}
for(Package subPackage : profile.getNestedPackages()) {
if(subPackage instanceof Profile) {
Profile subProfile = (Profile)subPackage;
findAllStereotypes(subProfile, result);
}
}
}
private static EPackage umlMetamodel = UMLPackage.eINSTANCE;
public static List<EClass> getAllExtendedMetaclasses(Stereotype stereotype, boolean concreteClassesOnly) {
List<Class> extendedMetaclasses = stereotype.getAllExtendedMetaclasses();
Set<EClass> allMetaclasses = new LinkedHashSet<EClass>();
for(Class extendedMetaclass : extendedMetaclasses) {
EClass UMLEClass = findEClass(extendedMetaclass);
allMetaclasses.addAll(EMFHelper.getSubclassesOf(UMLEClass, concreteClassesOnly));
}
return new LinkedList<EClass>(allMetaclasses);
}
private static EClass findEClass(Class metaclass) {
for(EClassifier classifier : umlMetamodel.getEClassifiers()) {
if(classifier instanceof EClass) {
if(classifier.getName().equals(metaclass.getName())) {
return (EClass)classifier;
}
}
}
return null;
}
/**
* Searchs the association containing a member end matching the given name
* in the given stereotype, and returns it if it is typed by a stereotype
*
* TODO : This method can probably be used in a single specific case and should probably not be here
*
* @param stereotype
* @param associationName
* @return
*/
public static Property findStereotypedProperty(Stereotype stereotype, String associationName) {
//associations loop
associations: for(Association association : stereotype.getAssociations()) {
//memberEnds loop
for(Property memberEnd : association.getMemberEnds()) {
if(memberEnd.getType() == stereotype) {
//oppositeEnds loop
for(Property oppositeEnd : association.getMemberEnds()) {
if(oppositeEnd != memberEnd && oppositeEnd.getName().equals(associationName) && !association.getOwnedEnds().contains(oppositeEnd)) {
if(oppositeEnd.getType() instanceof Stereotype) {
return oppositeEnd;
}
break associations;
}
}
}
}
}
//FIXME : Do we really need to browse associations first ?
//Search for properties which are not associations
for(Property property : stereotype.getAllAttributes()) {
if(property.getName().equals(associationName)) {
if(property.getType() instanceof Stereotype) {
return property;
}
}
}
return null;
}
}