blob: 9178aed59264646cfced0cf5cce2ead64b2d111e [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2016 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:
*
* Mauricio Alferez (mauricio.alferez@cea.fr) CEA LIST - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.requirements.languagecreationtools.domainModel2Profile.generator;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.WrappedException;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.papyrus.requirements.languagecreationtools.domainModel2Profile.utils.Utils;
import org.eclipse.papyrus.requirements.preferences.Activator;
import org.eclipse.papyrus.requirements.preferences.PreferenceConstants;
import org.eclipse.uml2.uml.Abstraction;
import org.eclipse.uml2.uml.AggregationKind;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.EnumerationLiteral;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.Model;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PackageableElement;
import org.eclipse.uml2.uml.PrimitiveType;
import org.eclipse.uml2.uml.Profile;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.resource.UMLResource;
import org.eclipse.uml2.uml.resources.util.UMLResourcesUtil;
/**
* Generates an UML profile based on a UML domain model
*
*/
public class ProfileGenerator {
protected static ResourceSet resourceSet;
// Output profile created based on the domain model
protected Profile profile;
// Input package representing the domain model
protected Package sourcePackage;
// File of the sourcePackage
protected File sourceModelFile;
// Generation options
private static String NSURI_PREFIX = "https://www.eclipse.org/papyrus/";
private static boolean GENERATE_ABSTRACTIONS_MODEL = false;
private static boolean FROM_ASSOCIATIONS_TO_STEREOTYPES = false;
private static boolean FROM_ASSOCIATIONS_TO_ASSOCIATIONS = false;
private static String METACLASS_EXTENDED_BY_NODES = "none"; //UMLPackage.Literals.CLASS.getName();
private static String METACLASS_EXTENDED_BY_EDGES = "none";//= UMLPackage.Literals.ASSOCIATION.getName();
// Debug=true will automatically refresh the project to show the output
// files.
private static boolean DEBUG = true;
protected Stereotype baseNodeStereotype;
protected Stereotype baseEdgeStereotype;
protected Map<Element, Element> mapClassNodes = new HashMap<Element, Element>();
protected Map<Element, Element> mapAssociationsEdges = new HashMap<Element, Element>();
protected Map<Element, Element> mapProfileAssociations2MMAssociations = new HashMap<Element, Element>();
protected Map<Element, Element> mapPrimitiveTypesNodes = new HashMap<Element, Element>();
protected Map<Element, Element> mapEnumerationsNodes = new HashMap<Element, Element>();
protected Map<Element, Element> mapPackagesNodes = new HashMap<Element, Element>();
protected List<PrimitiveType> listStandardPrimitiveTypes = new ArrayList<PrimitiveType>();
protected String outputDirectoryPath;
protected List<Generalization> listInternalGeneralizations = new ArrayList<Generalization>();
protected List<Association> listAssociations = new ArrayList<Association>();
protected Model abstractionsModel;
/**
* Constructor to be called by Eclipse, takes into account generation
* preferences in the Papyrus for Requirements preference page.
*
* @param selectedPackage The UML domain model
*/
public ProfileGenerator(Package selectedPackage) {
DEBUG = false;
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
NSURI_PREFIX = store.getString(PreferenceConstants.NSURI_PREFIX);
GENERATE_ABSTRACTIONS_MODEL = store.getBoolean(PreferenceConstants.GENERATE_ABSTRACTIONS_MODEL);
FROM_ASSOCIATIONS_TO_STEREOTYPES = store.getBoolean(PreferenceConstants.FROM_ASSOCIATIONS_TO_STEREOTYPES);
FROM_ASSOCIATIONS_TO_ASSOCIATIONS =store.getBoolean(PreferenceConstants.FROM_ASSOCIATIONS_TO_ASSOCIATIONS);
// METACLASS_EXTENDED_BY_NODES =
// store.getString(PreferenceConstants.METACLASS_EXTENDED_BY_NODES);
// //by default "none"
// METACLASS_EXTENDED_BY_EDGES =
// store.getString(PreferenceConstants.METACLASS_EXTENDED_BY_EDGES);
// //by default "none"
sourcePackage = selectedPackage;
URI uri = sourcePackage.eResource().getURI();
sourceModelFile = Utils.getFileFromURI(uri);
outputDirectoryPath = sourceModelFile.getParent();
setResourceSet();
}
/**
* Constructor to be used in an standalone Java application. It does not takes
* into account preferences in the Papyrus for Requirements preference page.
*
* @param _inputModelPath The path of the input model
* @param _outputDirectoryPath The path to save the profile and if the abstractions model
* @param _prefix the NSURI prefix used in the profile
*/
public ProfileGenerator(String _inputModelPath, String _outputDirectoryPath, String _prefix) {
DEBUG = true;
NSURI_PREFIX = _prefix;
ResourceSet resourceSetSourceModel = new ResourceSetImpl();
UMLResourcesUtil.init(resourceSetSourceModel);
URI _inputModelURI = URI.createFileURI(new File(_inputModelPath).getAbsolutePath());
Resource r = resourceSetSourceModel.getResource(_inputModelURI, true);
sourcePackage = (Package) EcoreUtil.getObjectByType(r.getContents(), UMLPackage.Literals.PACKAGE);
sourceModelFile = new File(_inputModelPath).getAbsoluteFile();
outputDirectoryPath = new File(_outputDirectoryPath).getAbsolutePath();
setResourceSet();
}
protected void setResourceSet() {
resourceSet = new ResourceSetImpl();
UMLResourcesUtil.init(resourceSet);
}
public void generate() {
String nameOfAbstractionsModel = sourcePackage.getName() + "_refinement";
String nameOfProfile = sourcePackage.getName();
setProfile(Utils.createProfile(nameOfProfile, NSURI_PREFIX + sourcePackage.getName()));
if (!METACLASS_EXTENDED_BY_NODES.contentEquals("none")) {
METACLASS_EXTENDED_BY_NODES= UMLPackage.Literals.CLASS.getName();
baseNodeStereotype = Utils.createStereotype(profile, "baseNodeStereotype", true);
}
if (!METACLASS_EXTENDED_BY_EDGES.contentEquals("none")) {
METACLASS_EXTENDED_BY_EDGES= UMLPackage.Literals.ASSOCIATION.getName();
baseEdgeStereotype = Utils.createStereotype(profile, "baseEdgeStereotype", true);
}
visitPackagedElements(sourcePackage);
String[] requiredPrimitiveTypes = { "Boolean", "Real", "String", "Integer", "UnlimitedNatural" };
createStandardPrimitiveTypes(requiredPrimitiveTypes);
createProfiles(mapClassNodes);
createProfiles(mapPrimitiveTypesNodes);
createProfiles(mapEnumerationsNodes);
if (FROM_ASSOCIATIONS_TO_STEREOTYPES) {
createProfiles(mapAssociationsEdges);
}
if (FROM_ASSOCIATIONS_TO_ASSOCIATIONS) {
createAssociations();
}
createEnumerationLiterals();
createTagDefinitions();
createInternalGeneralizations();
if (!METACLASS_EXTENDED_BY_NODES.contentEquals("none")
&& referenceMetaclass(profile, METACLASS_EXTENDED_BY_NODES) != null) {
createGeneralizations(mapClassNodes.keySet(), baseNodeStereotype);
Utils.createExtension(referenceMetaclass(profile, METACLASS_EXTENDED_BY_NODES), baseNodeStereotype, false);
}
if (!METACLASS_EXTENDED_BY_EDGES.contentEquals("none")
&& referenceMetaclass(profile, METACLASS_EXTENDED_BY_NODES) != null) {
createGeneralizations(mapAssociationsEdges.keySet(), baseEdgeStereotype);
Utils.createExtension(referenceMetaclass(profile, METACLASS_EXTENDED_BY_EDGES), baseEdgeStereotype, false);
}
Utils.defineProfile(profile);
for (Element element : mapPackagesNodes.keySet()) {
if (element instanceof Profile) {
Utils.defineProfile((Profile) element);
}
}
save(profile, outputDirectoryPath, nameOfProfile, UMLResource.PROFILE_FILE_EXTENSION);
if (GENERATE_ABSTRACTIONS_MODEL) {
abstractionsModel = Utils.createModel(nameOfAbstractionsModel);
createAbstractionsModel(mapClassNodes);
createAbstractionsModel(mapProfileAssociations2MMAssociations);
abstractionsModel.setURI(NSURI_PREFIX + nameOfAbstractionsModel);
save(abstractionsModel, outputDirectoryPath, nameOfAbstractionsModel, UMLResource.FILE_EXTENSION);
}
// Debug=true will automatically refresh the project to show the
// generated ...profile.uml file.
if (!DEBUG) {
Utils.refreshProject(sourceModelFile);
}
}
/**
* Creates associations between stereotypes in a profile, based on the
* associations in a domain model
*/
protected void createAssociations() {
Stereotype targetEndInProfile = null;
Stereotype sourceEndInProfile = null;
for (Association assocInMM : listAssociations) {
Property targetEndInMM = Utils.getTargetEnd(assocInMM);
// Property sourceEndInMM = targetEndInMM.getOtherEnd();
Property sourceEndInMM = Utils.getSourceEnd(assocInMM);
targetEndInProfile = getStereotypeByName(targetEndInMM.getType().getName());
sourceEndInProfile = getStereotypeByName(sourceEndInMM.getType().getName());
if (targetEndInProfile != null) {
Association assocInProfile = Utils.createAssociation(sourceEndInProfile, true,
AggregationKind.NONE_LITERAL, targetEndInMM.getName(), targetEndInMM.getLower(),
targetEndInMM.getUpper(), targetEndInProfile, true, AggregationKind.NONE_LITERAL,
sourceEndInMM.getName(), sourceEndInMM.getLower(), sourceEndInMM.getUpper());
assocInProfile.setName(assocInMM.getName());
mapProfileAssociations2MMAssociations.put(assocInProfile, assocInMM);
}
}
}
/**
* @param stereotypeName
* The not qualified name of a stereotype
* @return stereotype The stereotype with the stereotypeName in the
* Stereotypes - Class map (ex. key: Stereotype named Focus, value:
* Class named Focus).
*/
protected Stereotype getStereotypeByName(String stereotypeName) {
Stereotype stereotype=null;
for (Map.Entry<Element, Element> entry : mapClassNodes.entrySet()) {
if (entry.getKey() instanceof Stereotype && entry.getValue() instanceof Class) {
stereotype = (Stereotype) entry.getKey();
if (stereotype.getName().contentEquals(stereotypeName)){
return stereotype;
}
}
}
return null;
}
/**
* Generates the mapping model between source elements and produced
* elements. Each abstraction relationship can have many sources and many
* targets which is ideal to allow N 2 M mappings.
*
* @param map
* containing the element (value) in the domain model that
* originated the element (key) in the profile
*/
protected void createAbstractionsModel(Map<Element, Element> map) {
for (Map.Entry<Element, Element> entry : map.entrySet()) {
NamedElement key = (NamedElement) entry.getKey();
NamedElement value = (NamedElement) entry.getValue();
Abstraction _abstraction = UMLFactory.eINSTANCE.createAbstraction();
_abstraction.getSuppliers().add(key);
_abstraction.getClients().add(value);
_abstraction.setName(key.getName() + "-" + value.getName());
abstractionsModel.getPackagedElements().add(_abstraction);
}
}
protected void visitPackagedElements(Element element) {
if (element instanceof org.eclipse.uml2.uml.Package) {
org.eclipse.uml2.uml.Package packageInMetamodel = (org.eclipse.uml2.uml.Package) element;
for (Element e : packageInMetamodel.getPackagedElements()) {
visitPackagedElements(e);
}
} else if (element instanceof org.eclipse.uml2.uml.Class) {
mapClassNodes.put((Element) Utils.createStereotype(profile, ((NamedElement) element).getName(),
((Class) element).isAbstract()), element);
if (((Classifier) element).getGeneralizations() != null) {
for (Generalization generalization : ((Classifier) element).getGeneralizations()) {
listInternalGeneralizations.add(generalization);
}
}
} else if (element instanceof PrimitiveType) {
mapPrimitiveTypesNodes.put((Element) Utils.createPrimitiveType(profile, ((NamedElement) element).getName()),
element);
} else if (element instanceof Enumeration) {
mapEnumerationsNodes.put((Element) Utils.createEnumeration(profile, ((NamedElement) element).getName()),
element);
} else if (element instanceof Association) {
if (FROM_ASSOCIATIONS_TO_STEREOTYPES) {
mapAssociationsEdges.put(
(Element) Utils.createStereotype(profile, ((NamedElement) element).getName(), false), element);
}
listAssociations.add((Association)element);
}
}
protected void createInternalGeneralizations() {
for (Generalization generalizationInMM : listInternalGeneralizations) {
Classifier specificInMM = generalizationInMM.getSpecific();
Classifier generalInMM = generalizationInMM.getGeneral();
Stereotype specificInProfile = null;
Stereotype generalInProfile = null;
Set<Element> classNodesStereotypesInProfile = mapClassNodes.keySet();
for (Element e : classNodesStereotypesInProfile) {
Stereotype classNodeStereotypeInProfile = (Stereotype) e;
if (classNodeStereotypeInProfile.getName().contentEquals(specificInMM.getName())) {
specificInProfile = classNodeStereotypeInProfile;
} else if (classNodeStereotypeInProfile.getName().contentEquals(generalInMM.getName())) {
generalInProfile = classNodeStereotypeInProfile;
}
}
if (specificInProfile != null && generalInProfile != null) {
Utils.createGeneralization(specificInProfile, generalInProfile);
}
}
}
protected static org.eclipse.uml2.uml.Package load(URI uri) {
org.eclipse.uml2.uml.Package package_ = null;
try {
// Load the requested resource
Resource resource = resourceSet.getResource(uri, true);
// Get the first (should be only) package from it
package_ = (org.eclipse.uml2.uml.Package) EcoreUtil.getObjectByType(resource.getContents(),
UMLPackage.Literals.PACKAGE);
} catch (WrappedException we) {
System.err.println();
System.exit(1);
}
return package_;
}
public static PrimitiveType importPrimitiveType(org.eclipse.uml2.uml.Package package_, String name) {
org.eclipse.uml2.uml.Package umlLibrary = load(URI.createURI(UMLResource.UML_PRIMITIVE_TYPES_LIBRARY_URI));
PrimitiveType primitiveType = (PrimitiveType) umlLibrary.getOwnedType(name);
package_.createElementImport(primitiveType);
return primitiveType;
}
protected static org.eclipse.uml2.uml.Class referenceMetaclass(Profile profile, String metaclassName) {
Model umlMetamodel = (Model) load(URI.createURI(UMLResource.UML_METAMODEL_URI));
// my hack
Class metaclass = null;
if (umlMetamodel.getOwnedType(metaclassName) instanceof Class) {
metaclass = (Class) umlMetamodel.getOwnedType(metaclassName);
if (metaclass != null) {
profile.createMetaclassReference(metaclass);
}
}
return metaclass;
}
/**
* @param stereotypeInProfile
* @param propertyInClass
*/
protected void addEnumerationProperty(Stereotype stereotypeInProfile, Property propertyInClass) {
Set<Element> enumerationsInProfile = mapEnumerationsNodes.keySet();
for (Element e : enumerationsInProfile) {
Enumeration enumerationInProfile = (Enumeration) e;
if (enumerationInProfile.getName().contentEquals(propertyInClass.getType().getName())) {
Utils.createAttribute((Class) stereotypeInProfile, propertyInClass.getName(), enumerationInProfile, 0,
1, null);
}
}
}
/**
* @param stereotypeInProfile
* @param propertyInClass
*/
protected void addPrimitiveTypeProperty(Stereotype stereotypeInProfile, Property propertyInClass) {
boolean foundStandardPrimitiveType = false;
for (PrimitiveType standardPrimitiveType : listStandardPrimitiveTypes) {
if (standardPrimitiveType.getName().contentEquals(propertyInClass.getType().getName())) {
foundStandardPrimitiveType = true;
Utils.createAttribute((Class) stereotypeInProfile, propertyInClass.getName(), standardPrimitiveType,
propertyInClass.getLower(), propertyInClass.getUpper(), null);
break;
}
}
if (!foundStandardPrimitiveType) {
Set<Element> primitiveTypesInProfile = mapPrimitiveTypesNodes.keySet();
for (Element e : primitiveTypesInProfile) {
PrimitiveType primitiveTypeInProfile = (PrimitiveType) e;
if (primitiveTypeInProfile.getName().contentEquals(propertyInClass.getType().getName())) {
Utils.createAttribute((Class) stereotypeInProfile, propertyInClass.getName(),
primitiveTypeInProfile, propertyInClass.getLower(), propertyInClass.getUpper(), null);
}
}
}
}
protected void createEnumerationLiterals() {
for (Map.Entry<Element, Element> entry : mapEnumerationsNodes.entrySet()) {
Element key = entry.getKey();
Element value = entry.getValue();
if (key instanceof Enumeration && value instanceof Enumeration) {
Enumeration enumInProfile = (Enumeration) key;
Enumeration enumInMetamodel = (Enumeration) value;
for (EnumerationLiteral literalInMM : enumInMetamodel.getOwnedLiterals()) {
Utils.createEnumerationLiteral(enumInProfile, literalInMM.getName().replaceAll("\\s", ""));
}
}
}
}
protected void createGeneralizations(Set<Element> specificStereotypes, Stereotype generalStereotype) {
for (Element element : specificStereotypes) {
if (element instanceof Stereotype) {
Stereotype specificStereotype = (Stereotype) element;
if (specificStereotype.getGeneralizations().size() == 0) {
Utils.createGeneralization(specificStereotype, generalStereotype);
}
}
}
}
/**
* Creates a profile and sub-profiles based on the packages structure of the
* domain model. Each Package becomes a new profile.
*
* @param map
* in the map, the key is an Stereotype, PrimitiveType, Profile,
* etc. and the value is the Element that originated the creation
* of the key. For example, a Class X (the key) originated an
* Stereotype X (the value)
*
*/
protected void createProfiles(Map<Element, Element> map) {
boolean foundPackage;
for (Map.Entry<Element, Element> entry : map.entrySet()) {
Element key = entry.getKey();
Element value = entry.getValue();
Package package_ = value.getNearestPackage();
foundPackage = false;
for (Package nestedPackage : profile.getNestedPackages()) {
if (nestedPackage.getName().contentEquals(package_.getName())) {
foundPackage = true;
nestedPackage.getPackagedElements().add((PackageableElement) key);
break;
}
}
if (!foundPackage) {
Profile newSubProfile = Utils.createProfile(package_.getName(), NSURI_PREFIX + package_.getName());
mapPackagesNodes.put(newSubProfile, package_);
profile.getNestedPackages().add(newSubProfile);
newSubProfile.getPackagedElements().add((PackageableElement) key);
}
}
}
protected void createStandardPrimitiveTypes(String[] requiredPrimitiveTypes) {
for (String primitiveTypeName : requiredPrimitiveTypes) {
listStandardPrimitiveTypes.add(importPrimitiveType(profile, primitiveTypeName));
}
}
protected void createTagDefinitions() {
for (Map.Entry<Element, Element> entry : mapClassNodes.entrySet()) {
Element key = entry.getKey();
Element value = entry.getValue();
if (key instanceof Stereotype && value instanceof Class) {
Stereotype stereotypeInProfile = (Stereotype) key;
Class classInMetamodel = (Class) value;
for (Property propertyInClass : classInMetamodel.getOwnedAttributes()) {
if (propertyInClass.getType() instanceof PrimitiveType) {
addPrimitiveTypeProperty(stereotypeInProfile, propertyInClass);
} else if (propertyInClass.getType() instanceof Enumeration) {
addEnumerationProperty(stereotypeInProfile, propertyInClass);
}
}
}
}
}
/**
* @return the profile
*/
public Profile getProfile() {
return profile;
}
/**
* Save our refinement model to a file in the user-specified output
* directory
*
*/
protected void save(Package model, String outputDirectoryPath, String fileName, String fileExtension) {
URI outputURI = URI.createFileURI(outputDirectoryPath).appendSegment(fileName)
.appendFileExtension(fileExtension);
save(model, outputURI);
}
protected static void save(org.eclipse.uml2.uml.Package package_, URI uri) {
Resource resource = resourceSet.createResource(uri);
resource.getContents().add(package_);
try {
resource.save(null);
} catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
}
/**
* @param profile
* the profile to set
*/
public void setProfile(Profile profile) {
this.profile = profile;
}
}