| /***************************************************************************** |
| * 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; |
| } |
| } |