blob: 118f9ece7b25675531337be02b2d111ec8cab1f0 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2016, 2021 CEA LIST.
*
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Ansgar Radermacher ansgar.radermacher@cea.fr, initial implementation, bug 572601
*
*****************************************************************************/
package org.eclipse.papyrus.designer.uml.tools.utils;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.papyrus.designer.infra.base.StringConstants;
import org.eclipse.papyrus.designer.infra.base.StringUtils;
import org.eclipse.swt.widgets.Display;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Dependency;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.InstanceSpecification;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Namespace;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Parameter;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Relationship;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLPackage;
import org.eclipse.uml2.uml.resource.UMLResource;
/**
* A set of utility functions around elements
*/
public class ElementUtils {
/**
* Retrieve an element from a list of named elements via its name
*
* @param elementList
* a list of EObjects, of which some are UML named elements
* @param name
* the name to look for
* @return the first element from the list that has the same name, or null if none exists
*/
public static NamedElement getNamedElementFromList(EList<? extends EObject> elementList, String name) {
for (EObject element : elementList) {
if (element instanceof NamedElement) {
NamedElement namedElement = (NamedElement) element;
if ((namedElement.getName() != null) && namedElement.getName().equals(name)) {
return namedElement;
}
}
}
return null;
}
/**
* @param element
* an element which is owned by a model.
* @param subfolder
* the name of a sub-folder within root (created, if not
* existent)
* @return a reference to the sub folder within the root of the model that
* belongs to the passed element.
*/
public static Package getRoot(Element element, String subfolder) {
Package root = PackageUtil.getRootPackage(element);
if (root.getNestedPackage(subfolder) != null) {
return root.getNestedPackage(subfolder);
} else {
return root.createNestedPackage(subfolder);
}
}
/**
* this method returns the component type of an implementation. It is based
* on the modeling convention that implementations inherit from types.
* TODO: It currently returns the first abstract (direct) superclass
*
* @param clazz
* A component implementation
* @return the first abstract superclass
*/
public static Class componentType(Class clazz) {
if (clazz.isAbstract()) {
return clazz;
} else {
for (Class superclass : clazz.getSuperClasses()) {
if (superclass.isAbstract()) {
return superclass;
}
}
return null;
}
}
/**
* @param is
* an instance specification
* @return true, if an instance specification is a composite, i.e. has more than 1 slots
*/
public static boolean isComposite(InstanceSpecification is) {
return (is.getSlots().size() > 0);
}
/**
* Get an element via its qualified name. This function will find the first element with a
* matching qualified name within the resource set associated with the passed element
*
* @param element
* An element within a resource which in turn is part of the search resource set
* @param qualifiedName
* the qualified name of an element
* @return the found element or null
*/
public static NamedElement getQualifiedElementFromRS(Element element, String qualifiedName) {
return getQualifiedElementFromRS(element.eResource().getResourceSet(), qualifiedName);
}
/**
* Get an element via its qualified name. This function will find the first element with a
* matching qualified name within the resource set associated with the passed element.
* If the element is not found, load the passed URI into the resource set and try again
* The loading is done in the UI thread (see bug 572601) in case of a Papyrus model-set
*
* @param element
* An element within a resource which in turn is part of the search resource se
* @param uri
* an URI to load before searching an element
* @param qualifiedName
* the qualified name of an element
* @return the found element or null
*/
public static NamedElement getQualifiedElementFromRS(Element element, URI uri, String qualifiedName) {
URI uriList[] = { uri };
return getQualifiedElementFromRS(element, uriList, qualifiedName);
}
/**
* Get an element via its qualified name. This function will find the first element with a
* matching qualified name within the resource set associated with the passed element.
* If the element is not found, load the passed URI into the resource set and try again
* The loading is done in the UI thread (see bug 572601) in case of a Papyrus model-set
*
* @param element
* An element within a resource which in turn is part of the search resource set
* @param uriList
* a list (array) of URIs to load before searching an element
* @param qualifiedName
* the qualified name of an element
* @return the found element or null
*/
public static NamedElement getQualifiedElementFromRS(Element element, URI uriList[], String qualifiedName) {
ResourceSet set = element.eResource().getResourceSet();
for (URI uri : uriList) {
set.getResource(uri, true);
}
NamedElement ne = getQualifiedElementFromRS(set, qualifiedName);
return ne;
}
/**
* Provide a wrapper for the packageUtil functionality.
* The loading is done in the UI thread (see bug 572601) in case of a Papyrus model-set
*
* @param uri
* the URI of the resource to load
* @param element
* an element, used to determine resource set
* @return the contained package, or null, if not found
*/
public static Package loadPackage(URI uri, Element element) {
return loadPackage(uri, element.eResource().getResourceSet());
}
/**
* Provide a wrapper for the packageUtil functionality.
* The loading is done in the UI thread (see bug 572601) in case of a Papyrus model-set
*
* @param uri
* the URI of the resource to load
* @param set
* a resource set
* @return the contained package, or null, if not found
*/
public static Package loadPackage(URI uri, ResourceSet set) {
// load if and not yet loaded
if (set.getResource(uri, false) == null) {
// execute in UI thread
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
set.getResource(uri, true);
}
});
// resource is now already loaded, invocation of PackageUtil is now safe
}
return PackageUtil.loadPackage(uri, set);
}
/**
* loadPackage
* Get an element via its qualified name. This function will find the first element in the
* resource set that has this qualified name (whether imported or not)
*
* @param rs
* a resource set
* @param qualifiedName
* the qualified name of an element
* @return the found element or null
*/
public static NamedElement getQualifiedElementFromRS(ResourceSet rs, String qualifiedName) {
for (Resource resource : rs.getResources()) {
if (resource instanceof UMLResource) {
if (resource.getContents().size() > 0) {
EObject topLevelElem = resource.getContents().get(0);
if (topLevelElem instanceof Package) {
NamedElement ne = getQualifiedElement((Package) topLevelElem, qualifiedName);
if (ne != null) {
return ne;
}
}
}
}
}
return null;
}
/**
* Get an element via its qualified name. It will ignore imports
*
* @param root
* the root element (package) of a model
* @param qualifiedName
* the qualified name of an element, it may contains "*" segments as wildcard
* @return the found element or null
*/
public static NamedElement getQualifiedElement(Package root, String qualifiedName) {
String[] path = qualifiedName.split(NamedElement.SEPARATOR);
if (path[0].equals(root.getName()) || StringConstants.STAR.equals(path[0])) {
return getQualifiedElement(root, path, 1);
}
return null;
}
/**
* Retrieve an element via its qualified name within a root namespace.
*
* @return the found element, if it exists
*/
public static NamedElement getQualifiedElement(Namespace root, String[] path, int pos) {
if (root == null) {
return null;
}
if (pos == path.length) {
return root;
} else if (pos < path.length) {
for (NamedElement segmentElem : root.getMembers()) {
if (path[pos].equals(segmentElem.getName()) || StringConstants.STAR.equals(path[pos])) {
if (pos == path.length - 1) {
return segmentElem;
} else if (segmentElem instanceof Namespace) {
// requires further recursion
NamedElement foundElement = getQualifiedElement((Namespace) segmentElem, path, pos + 1);
// return, if not found
if (foundElement != null) {
return foundElement;
}
}
}
}
}
return null;
}
/**
* Simple check whether an element is in a different model than the passed
* package It will return true, whenever the the top elements do not match.
* This is always true, if the 2nd belongs to a different model, whether
* imported or not. This distinction is however not required in our context.
*/
public static boolean isElementInDifferentModel(Package model, NamedElement namedElement) {
return model != PackageUtil.getRootPackage(namedElement);
}
public static <T extends EObject> EList<T> getAllElementsOfType(Element examineElement, java.lang.Class<T> clazz) {
EList<Element> visitedPackages = new BasicEList<>();
return getAllElementsOfType(examineElement, clazz, visitedPackages);
}
@SuppressWarnings("unchecked")
public static <T extends EObject> EList<T> getAllElementsOfType(Element examineElement, java.lang.Class<T> clazz, EList<Element> visitedPackages) {
EList<T> list = new UniqueEList<>();
for (Element element : examineElement.allOwnedElements()) {
if (element instanceof Package) {
if (!visitedPackages.contains(element)) {
visitedPackages.add(element);
list.addAll(getAllElementsOfType(element, clazz, visitedPackages));
}
} else if (clazz.isInstance(element)) {
list.add((T) element);
}
}
return list;
}
/**
* Convenience function: Declare a dependency from source to destination. The function checks,
* if a dependency already exists to avoid double dependencies.
*
* @param source
* source type of the dependency
* @param dest
* destination of the dependency
*/
public static void declareDependency(Type source, Type dest) {
// check, if a relationship already exists
for (Relationship dependency : source.getRelationships(UMLPackage.eINSTANCE.getDependency())) {
if (((Dependency) dependency).getSuppliers().contains(dest)) {
return;
}
}
source.createDependency(dest);
}
/**
* This method returns all types that are referenced by a classifier. This includes
* attribute types, types within operations as well as inherited types.
* This is useful to generate the #includes
* (which is used by the Acceleo code within the packaging plugin)
*/
public static EList<Classifier> getReferencedClassifiers(Classifier classifier) {
EList<Classifier> list = new UniqueEList<>();
list.addAll(classifier.parents());
if (classifier instanceof Class) {
// get classifiers referenced by attributes
for (Operation operation : ((Class) classifier).getOwnedOperations()) {
for (Parameter parameter : operation.getOwnedParameters()) {
Type type = parameter.getType();
if (type instanceof Classifier) {
list.add((Classifier) type);
}
}
}
// get classifiers referenced by attributes
for (Property attribute : ((Class) classifier).getOwnedAttributes()) {
Type type = attribute.getType();
if (type instanceof Classifier) {
list.add((Classifier) type);
}
}
}
return list;
}
public static EList<Namespace> usedNamespaces(NamedElement element) {
EList<Namespace> list = new BasicEList<>(element.allNamespaces());
if (list.size() < 1) {
return null;
}
list.remove(list.size() - 1); // remove last element (top-level)
return list;
}
/**
* A small helper function that makes names compliant with variable
* names in programming languages such as C++ or Java
*/
public static String varName(NamedElement element) {
return StringUtils.varName(element.getName());
}
/**
* Get the XML (URI) Id of an element within your model
* Useful for transmitting model references
*
* @param element
* a UML element
* @return the XML id, if the passed element is stored in an XML resource
*/
public static String xmlID(Element element) {
Resource resource = element.eResource();
// TODO: use EcoreUtil getURI (InternalEObject) instead?
if (resource instanceof XMLResource) {
XMLResource xmlResource = (XMLResource) resource;
return xmlResource.getID(element);
}
return null;
}
}