/*****************************************************************************
* Copyright (c) 2008 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:
* Chokri Mraidha (CEA LIST) Chokri.Mraidha@cea.fr - Initial API and implementation
* Patrick Tessier (CEA LIST) Patrick.Tessier@cea.fr - modification
*
*****************************************************************************/
package org.eclipse.papyrus.uml.profile.definition;
import java.util.ArrayList;
import java.util.Iterator;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EcoreFactory;
import org.eclipse.papyrus.uml.profile.Message;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.DataType;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Type;
/**
* <p>
* this class is used to manage the redefinition of profiles:
* </p>
* The normal definition in UML is like this:
* <ul>
* <li>Stereotype-->Eclass</li>
* <li>Enumeration-->EEnumeration :local copy
* <li>Datatype-->EClass</li>
* <li>Property--> EReference or EAttribute with ref on local copy</li>
* <li>PrimitiveType-> Edatatype : local copy</li>
* </ul>
* In papyrus:
* <ul>
* <li>Stereotype-->Eclass
* <li>Enumeration-->EEnumeration:local copy
* <li>Datatype-->EDatatype: local copy
* <li>Property--> EReference or EAttribute with ref on direct definition
* <li>PrimitiveType-> Edatatype : local copy
* </ul>
*/
public class ProfileRedefinition {
/**
* this method is used to redefine profile even if it contains subpackages
* or subprofiles
*
* @param thepackage
* the given profile that we want to define
* @param definitionAnnotation
* the definition annotation that is used to create the version
* annotation
*/
public static void redefineProfile(Package thepackage, PapyrusDefinitionAnnotation definitionAnnotation) {
// he wants to define
if(thepackage instanceof Profile) {
Profile profile = (Profile)thepackage;
// get profile definition
EPackage profileDefinition = profile.getDefinition();
// collect all EClassifier of the definition
ArrayList<EClassifier> tempList = new ArrayList<EClassifier>();
for(int i = 0; i < profileDefinition.getEClassifiers().size(); i++) {
tempList.add((EClassifier)profileDefinition.getEClassifiers().get(i));
}
// for all EClass
Iterator<EClassifier> eClassIter = tempList.iterator();
while(eClassIter.hasNext()) {
EClassifier eclassifier = eClassIter.next();
if(eclassifier instanceof EClass && !isDataTypeDefinition(eclassifier)) {
// redefine Eclass
redefineEclass((EClass)eclassifier);
}
}
// add profile definition annotation
if(definitionAnnotation != null) {
profile.getDefinition().getEAnnotations().add(definitionAnnotation.convertToEAnnotation());
}
}
Iterator<Package> it = thepackage.getNestedPackages().iterator();
while(it.hasNext()) {
Package p = it.next();
ProfileRedefinition.redefineProfile(p, definitionAnnotation);
}
}
/**
* redefine only real definition or do nothing
*
* @param eclass
* the given eclass that we want to redefine
*/
public static void redefineEclass(EClass eclass) {
if(isADirectDefinition(eclass)) {
// 1. redefine inheritances
EList<EClass> eSuperTypes = eclass.getESuperTypes();
/* copy in order to avoid concurrent access */
ArrayList<EClass> superTypesList = new ArrayList<EClass>();
for(int j = 0; j < eSuperTypes.size(); j++) {
superTypesList.add((EClass)eSuperTypes.get(j));
}
// for each super types :we test if this is a direct definition
// if not we remove the local copy and replace by direct definition
Iterator<EClass> superIter = superTypesList.iterator();
while(superIter.hasNext()) {
EClass currentSuperClass = superIter.next();
if(!isADirectDefinition(currentSuperClass)) {
EClass directSuperClass = (EClass)lookForDirectDefinitionFrom(currentSuperClass);
eclass.getESuperTypes().remove(currentSuperClass);
eclass.getESuperTypes().add(directSuperClass);
}
}
// 2.redefine eReferences
// temp copy of the list
Iterator<EReference> iterReference = eclass.getEReferences().iterator();
ArrayList<EReference> referenceList = new ArrayList<EReference>();
while(iterReference.hasNext()) {
referenceList.add(iterReference.next());
}
// for each reference of the EClass
Iterator<EReference> refIterator = referenceList.iterator();
while(refIterator.hasNext()) {
redefineEReference(refIterator.next(), eclass.getEPackage());
}
// 3. redefine EAttributes
Iterator<EAttribute> eattIterator = eclass.getEAllAttributes().iterator();
// for each reference of the EClass
while(eattIterator.hasNext()) {
redefineEAttribute(eattIterator.next(), eclass.getEPackage());
}
}
}
/**
* this class is used to redefine EReference with direct definition
*
* @param eReference
* the given EReference that we want to redefine
*/
public static void redefineEReference(EReference eReference, EPackage profileDefinition) {
EReference oldEOpposite = eReference.getEOpposite();
EClassifier oldType = eReference.getEType();
// 2.1 the type is an EClass
if(oldType instanceof EClass) {
// redefine type
eReference.setContainment(false);
if(!isDataTypeDefinition(oldType)) {
eReference.setEType(lookForDirectDefinitionFrom((EClass)oldType));
// redefine the Opposite
if(oldEOpposite != null)
eReference.setEOpposite(lookForEquivalentEreference((EClass)eReference.getEType(), oldEOpposite));
}
}
if(isDataTypeDefinition(oldType)) {
EAttribute eatt = createEAttribute(eReference.getEContainingClass(), eReference);
eatt.setEType(createDataType(profileDefinition, getUMLClassifierFromDefinition(oldType)));
eatt.setDefaultValue(eReference.getDefaultValue());
eatt.setDefaultValueLiteral(eReference.getDefaultValueLiteral());
eReference.getEContainingClass().getEStructuralFeatures().remove(eReference);
}
}
/**
* this class is used to redefine EAttribute with direct definition
*
* @param eAttribute
* the given EAttribute that we want to redefine
* @param profileDefinition
* the epackage that contains the Eclass , container of this
* Eattribute
*/
public static void redefineEAttribute(EAttribute eAttribute, EPackage profileDefinition) {
EClassifier oldEType = eAttribute.getEType();
if(isDataTypeDefinition(oldEType)) {
eAttribute.setEType(createDataType(profileDefinition, getUMLClassifierFromDefinition(oldEType)));
}
}
/**
* return id this Eclass is the real Definition
*
* @param eclass
* the eclass that we want to test
* @return true if this is the real definition or not is this is a local
* copy
*/
public static boolean isADirectDefinition(EClass eclass) {
if(eclass.getEAnnotations().size() > 0) {
EAnnotation eAnnotation = eclass.getEAnnotations().get(0);
if(eAnnotation.getReferences().size() > 0) {
if(!(eAnnotation.getReferences().get(0) instanceof org.eclipse.uml2.uml.Stereotype)) {
String errMsg = "Problem because of the definition of " + eclass.getName() + " in "
+ eclass.getEPackage().getNsURI();
Message.error(errMsg);
}
org.eclipse.uml2.uml.Stereotype theStereotype = (org.eclipse.uml2.uml.Stereotype)eAnnotation
.getReferences().get(0);
if(theStereotype.getDefinition().equals(eclass)) {
return true;
}
}
}
return false;
}
/**
* look for the real definition of the stereotype where the EClass may be
* the definition
*
* @param eclass
* that maybe the real definition (maybe a local copy of
* definition)
* @return the real definition or the itself
*/
public static EClassifier lookForDirectDefinitionFrom(EClassifier eClassifier) {
if(eClassifier.getEAnnotations().size() > 0) {
EAnnotation eAnnotation = eClassifier.getEAnnotations().get(0);
if(eAnnotation.getReferences().size() > 0) {
org.eclipse.uml2.uml.Stereotype theStereotype = (org.eclipse.uml2.uml.Stereotype)eAnnotation
.getReferences().get(0);
return theStereotype.getDefinition();
}
}
return eClassifier;
}
/**
* this method is used to look for an EReference equivalent to a given
* EReference same name and type
*
* @param eclass
* the given EClass where we look for the equivalent EReference
* @param eReference
* the given EReference
* @return the return EReference or null
*/
private static EReference lookForEquivalentEreference(EClass eclass, EReference eReference) {
Iterator<EReference> refIterator = eclass.getEReferences().iterator();
while(refIterator.hasNext()) {
EReference currentEReference = refIterator.next();
if(currentEReference.getName().equals(eReference.getName())) {
if(currentEReference.getEType().getName().endsWith(eReference.getEType().getName())) {
return currentEReference;
}
}
}
return null;
}
/**
* this method is used to suppress all local copy of EClass in each Profile.
*
* @param thePackage
* that we want to clean
*/
public static void cleanProfile(Package thePackage) {
if(thePackage instanceof Profile) {
Profile profile = (Profile)thePackage;
// get profile definition
EPackage profileDefinition = profile.getDefinition();
// collect all EClassifier of the definition
ArrayList<EClassifier> tempList = new ArrayList<EClassifier>();
for(int i = 0; i < profileDefinition.getEClassifiers().size(); i++) {
tempList.add((EClassifier)profileDefinition.getEClassifiers().get(i));
}
// for all EClass
Iterator<EClassifier> eClassIter = tempList.iterator();
while(eClassIter.hasNext()) {
EClassifier eclassifier = eClassIter.next();
if(eclassifier instanceof EClass) {
if(isDataTypeDefinition(eclassifier)) {
profileDefinition.getEClassifiers().remove(eclassifier);
}
// this is a direct Definition?
else if(!isADirectDefinition((EClass)eclassifier)) {
// no, so it is removed
profileDefinition.getEClassifiers().remove(eclassifier);
}
}
}
}
Iterator<Package> it = thePackage.getNestedPackages().iterator();
while(it.hasNext()) {
Package p = it.next();
ProfileRedefinition.cleanProfile(p);
}
}
/**
* Create a Edataptype from a datatype if it does not exist
*
* @param profileDefinition
* the Epackage where we want to create the Edatatype
* @param currentUmlType
* from this, the Edatatype will be created
* @return the created datatype
*/
public static EDataType createDataType(EPackage profileDefinition, Type currentUmlType) {
/* if eDataType already exists */
Iterator<EClassifier> iter = profileDefinition.getEClassifiers().iterator();
while(iter.hasNext()) {
EClassifier current = iter.next();
if(current instanceof EDataType) {
if(current.getName().equals(currentUmlType.getName())) {
// instance class name specified for primitiveTypes.
current.setInstanceClassName("java.lang.String");
return (EDataType)current;
}
}
}
// no, it has to be created
EDataType dataTypeDef = EcoreFactory.eINSTANCE.createEDataType();
dataTypeDef.setName(currentUmlType.getName());
dataTypeDef.setInstanceClassName("java.lang.String");
dataTypeDef.setSerializable(true);
EAnnotation umlAnnotation = EcoreFactory.eINSTANCE.createEAnnotation();
umlAnnotation.setSource("http://www.eclipse.org/uml2/2.0.0/UML");
umlAnnotation.getReferences().add(currentUmlType);
dataTypeDef.getEAnnotations().add(umlAnnotation);
profileDefinition.getEClassifiers().add(dataTypeDef);
return dataTypeDef;
}
/**
* this method is used to created an EAttribute from an Ereference
*
* @param container
* the Eclass that will contain the eattribute
* @param eReference
* from this, the eattribute will be created
* @return the created Eattribute
*/
public static EAttribute createEAttribute(EClass container, EReference eReference) {
EAttribute eAttribute = EcoreFactory.eINSTANCE.createEAttribute();
eAttribute.setEType(eReference.getEType());
eAttribute.setName(eReference.getName());
eAttribute.setChangeable(eReference.isChangeable());
eAttribute.setLowerBound(eReference.getLowerBound());
eAttribute.setUpperBound(eReference.getUpperBound());
eAttribute.setOrdered(eReference.isOrdered());
eAttribute.setDerived(eReference.isDerived());
eAttribute.setTransient(eReference.isTransient());
eAttribute.setUnique(eReference.isUnique());
eAttribute.setUnsettable(eReference.isUnsettable());
eAttribute.setVolatile(eReference.isVolatile());
container.getEStructuralFeatures().add(eAttribute);
return eAttribute;
}
/**
* test if the Eclass is a definition of a Datatype
*
* @param eclass
* that we want to test
* @return true if this is the defintion of a Datatype, false other
*/
public static boolean isDataTypeDefinition(EClassifier eclass) {
if(eclass.getEAnnotations().size() > 0) {
EAnnotation eAnnotation = eclass.getEAnnotations().get(0);
if(eAnnotation.getReferences().size() > 0) {
org.eclipse.uml2.uml.Classifier theClassifier = (org.eclipse.uml2.uml.Classifier)eAnnotation
.getReferences().get(0);
if(theClassifier instanceof DataType && !(theClassifier instanceof Enumeration)) {
return true;
}
}
}
return false;
}
/**
* this method is used to obtain the classifier from its definition
*
* @param eclass
* that is a definition
* @return the classifier that produce this definition
*/
public static Classifier getUMLClassifierFromDefinition(EClassifier eclass) {
if(eclass.getEAnnotations().size() > 0) {
EAnnotation eAnnotation = eclass.getEAnnotations().get(0);
if(eAnnotation.getReferences().size() > 0) {
org.eclipse.uml2.uml.Classifier theClassifier = (org.eclipse.uml2.uml.Classifier)eAnnotation
.getReferences().get(0);
return theClassifier;
}
}
return null;
}
}