diff options
Diffstat (limited to 'plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/EMFHelper.java')
-rw-r--r-- | plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/EMFHelper.java | 2246 |
1 files changed, 1124 insertions, 1122 deletions
diff --git a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/EMFHelper.java b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/EMFHelper.java index feef59d314d..bd806fec1c2 100644 --- a/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/EMFHelper.java +++ b/plugins/infra/emf/org.eclipse.papyrus.infra.emf/src/org/eclipse/papyrus/infra/emf/utils/EMFHelper.java @@ -1,1122 +1,1124 @@ -/*****************************************************************************
- * Copyright (c) 2010, 2016 CEA LIST, Christian W. Damus, and others.
- *
- * 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
- * Christian W. Damus (CEA) - filter out EObjects that are Resources (CDO)
- * Christian W. Damus (CEA) - Support read-only state at object level (CDO)
- * Christian W. Damus (CEA) - bugs 323802, 429826, 408491, 432813, 422257
- * Christian W. Damus - bugs 469188, 485220
- *
- *****************************************************************************/
-package org.eclipse.papyrus.infra.emf.utils;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.emf.common.util.TreeIterator;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.common.util.WrappedException;
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EClassifier;
-import org.eclipse.emf.ecore.EDataType;
-import org.eclipse.emf.ecore.EEnum;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EPackage;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.EStructuralFeature.Setting;
-import org.eclipse.emf.ecore.EcorePackage;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.resource.ResourceSet;
-import org.eclipse.emf.ecore.resource.URIConverter;
-import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
-import org.eclipse.emf.ecore.util.EcoreUtil;
-import org.eclipse.emf.ecore.xmi.XMIResource;
-import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
-import org.eclipse.emf.edit.domain.EditingDomain;
-import org.eclipse.papyrus.infra.core.resource.IReadOnlyHandler;
-import org.eclipse.papyrus.infra.core.resource.IReadOnlyHandler2;
-import org.eclipse.papyrus.infra.core.resource.ReadOnlyAxis;
-import org.eclipse.papyrus.infra.core.services.ServiceException;
-import org.eclipse.papyrus.infra.core.utils.ServiceUtils;
-import org.eclipse.papyrus.infra.emf.Activator;
-import org.eclipse.papyrus.infra.tools.util.PlatformHelper;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-
-/**
- * A Helper class for manipulating EMF Objects
- *
- * @author Camille Letavernier
- */
-// TODO : Check implementations. Most of them are old and don't always match the specification
-public class EMFHelper {
-
- /**
- * Returns the EClass corresponding to the given nsUri and className
- *
- * @param nsUri
- * The NSURI of the EClass' EPackage
- * @param className
- * The EClass' name
- * @return
- * The EClass instance, or null if the EClass couldn't be found
- */
- public static EClass getEClass(final String nsUri, final String className) {
- EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsUri);
- if (ePackage == null) {
- Activator.log.warn("Cannot find an EPackage matching the nsURI " + nsUri); //$NON-NLS-1$
- return null;
- }
- return getEClass(ePackage, className);
- }
-
- /**
- * Return the EClass corresponding to the given EPackage and className
- *
- * @param metamodel
- * The EClass' EPackage
- * @param className
- * The EClass' name
- * @return
- * The EClass instance, or null if the EClass couldn't be found
- */
- public static EClass getEClass(final EPackage metamodel, final String className) {
- EClassifier classifier = metamodel.getEClassifier(className);
- if (classifier == null) {
- Activator.log.warn("Classifier " + className + " not found in metamodel " + metamodel.getName() + " (" + metamodel.getNsURI() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
- }
- if (classifier instanceof EClass) {
- return (EClass) classifier;
- } else {
- Activator.log.warn("Classifier " + className + " in " + metamodel.getName() + " (" + metamodel.getNsURI() + ") is not an EClass"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
- }
-
- return null;
- }
-
- /**
- * Tests if an Object is an instance of the given EClass
- *
- * @param element
- * The EObject to test
- * @param className
- * The name of the EClass
- * @param metamodel
- * The EPackage owning the EClass
- * @return
- * True if the EObject is an instance of the EClass, or of one of the EClass' subtypes
- */
- public static boolean isInstance(final EObject element, final String className, final EPackage metamodel) {
-
- EClassifier theClass = metamodel.getEClassifier(className);
-
- if (theClass == null) {
- String message = String.format("Class %s not found in metamodel: %s (%s)", className, metamodel.getName(), metamodel.getNsURI());//$NON-NLS-1$
- Activator.log.warn(message);
- return false;
- }
-
- return theClass.isInstance(element);
- }
-
-
- /**
- * Tests if an Object is an instance of the given EClass
- *
- * @param element
- * The EObject to test
- * @param className
- * The name of the EClass
- * @param metamodel
- * The URI of the EPackage owning the EClass
- * @return
- * True if the EObject is an instance of the EClass, or of one of the EClass' subtypes
- */
- public static boolean isInstance(EObject selectedItem, String className, String nsUri) {
- EClass actualEClass = selectedItem.eClass();
-
- // Exact match
- if (isExactMatch(actualEClass, className, nsUri)) {
- return true;
- }
-
- List<EClass> allSuperTypes = actualEClass.getEAllSuperTypes();
- for (EClass eClass : allSuperTypes) {
- if (isExactMatch(eClass, className, nsUri)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Tests whether the given EClass has the given className and belongs to the EPackage represented by nsUri
- *
- * @param eClass
- * The EClass to test
- * @param className
- *
- * @param nsUri
- * @return
- * True if the EClass' name is className and the EClass' EPackage's nsURI is nsUri
- */
- private static boolean isExactMatch(EClass eClass, String className, String nsUri) {
- return className.equals(eClass.getName()) && nsUri.equals(eClass.getEPackage().getNsURI());
- }
-
- /**
- * Tests if the given eClass is a Subclass of fromClass
- * Also returns true when eClass == fromClass
- *
- * @param eClass
- * @param fromClass
- * @return
- * true if eClass is a subclass of fromClass
- */
- public static boolean isSubclass(final EClass eClass, final EClass fromClass) {
- // Everything is an EObject
- if (eClass != null && fromClass == EcorePackage.eINSTANCE.getEObject()) {
- return true;
- }
-
- if (eClass == fromClass) {
- return true;
- }
-
- List<EClass> superTypes = eClass.getEAllSuperTypes();
- if (superTypes.contains(fromClass)) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Returns the EObject corresponding to the input object
- * Tests if the input is an EObject, or if it is Adaptable
- * to an EObject
- *
- * @param source
- * @return An EObject corresponding to the input source, or null
- * if the EObject could not be resolved
- */
- public static EObject getEObject(final Object source) {
-
- // Support for EMF 0.2 CustomizedTree: The TreeElements are IAdaptable, but the
- // EObject adapter for some of them (e.g. reference node) is not what we need
- Object resolved = Activator.getDefault().getEObjectResolver().resolve(source);
- if ((resolved != source) && isEMFModelElement(resolved)) {
- return (EObject) resolved;
- }
-
- // General case
- if (isEMFModelElement(source)) {
- return (EObject) source;
- }
-
- // Try to get an intrinsic adapter
- if (source instanceof IAdaptable) {
- EObject eObject = ((IAdaptable) source).getAdapter(EObject.class);
- if (eObject == null) { // EMF Facet 0.1
- eObject = ((IAdaptable) source).getAdapter(EReference.class);
- }
-
- if (eObject != null) {
- return asEMFModelElement(eObject); // in case the adapter is a CDOResource
- }
- }
-
- // External adapter (last ditch case)
- if (source != null) {
- return asEMFModelElement(Platform.getAdapterManager().getAdapter(source, EObject.class));
- }
-
- return null;
- }
-
- /**
- * Queries whether an {@code object} is an EMF model element, an instance of
- * some {@link EClass} from an EMF model. This isn't as simple as checking
- * whether the object is an {@link EObject} because there are edge cases
- * where objects are {@code EObject}s but shouldn't be treated as
- * "model content". But, a minimum requirement is that the {@code object} is
- * an {@link EObject}.
- *
- * @param object
- * an object
- * @return whether it is "model content"
- *
- * @see EMFHelper#asEMFModelElement(Object)
- */
- public static boolean isEMFModelElement(Object object) {
- return (object instanceof EObject) && !(object instanceof Resource);
- }
-
- /**
- * Casts an {@code object} as an EMF model element, if appropriate.
- *
- * @param object
- * an object
- * @return the object as an EMF model element, or {@code null} if it is not
- * an EMF model element
- *
- * @see #isEMFModelElement(Object)
- */
- public static EObject asEMFModelElement(Object object) {
- return isEMFModelElement(object) ? (EObject) object : null;
- }
-
- /**
- * Retrieve the EditingDomain for the given source object. The object is first
- * resolved to an EObject through #getEObject when possible.
- *
- * @param source
- * @return
- * The source object's editing domain, or null if it couldn't be found
- */
- public static EditingDomain resolveEditingDomain(final Object source) {
- return resolveEditingDomain(getEObject(source));
- }
-
- /**
- * Retrieve the EditingDomain for the given source EObject
- *
- * @param source
- * @return
- * The source eObject's editing domain, or null if it couldn't be found
- */
- public static EditingDomain resolveEditingDomain(final EObject source) {
- EditingDomain domain = AdapterFactoryEditingDomain.getEditingDomainFor(source);
- if (domain == null) {
- try {
- domain = ServiceUtils.getInstance().getTransactionalEditingDomain(null);
- } catch (ServiceException e) {
- // Ignore: We cannot find the domain
- }
- }
- return domain;
- }
-
- /**
- * Return the eClassifier' qualified name. The qualified name is obtained by the concatenation
- * of its package hierarchy with the class name, separated by the given separator
- *
- * @param eClassifier
- * @param separator
- * The separator used between each package name
- * @return
- * The EClassifier' qualified name
- */
- public static String getQualifiedName(final EClassifier eClassifier, final String separator) {
- return getQualifiedName(eClassifier.getEPackage(), separator) + separator + eClassifier.getName();
- }
-
- /**
- * Return the ePackage's qualified name. The qualified name is obtained by the concatenation
- * of its superPackage hierarchy with the ePackage name, separated by the given separator
- *
- * @param ePackage
- * @param separator
- * The separator used between each package name
- * @return
- * The EPackage's qualified name
- */
- public static String getQualifiedName(final EPackage ePackage, final String separator) {
- if (ePackage.getESuperPackage() == null) {
- return ePackage.getName();
- }
- return getQualifiedName(ePackage.getESuperPackage(), separator) + separator + ePackage.getName();
- }
-
-
- /**
- * Loads and returns the first EObject at the given URI.
- * The EObject is loaded in the given resourceSet.
- *
- * @param resourceSet
- * The ResourceSet in which the model will be loaded
- * @param uri
- * The URI describing the location of the model to load
- * @return
- * The first EObject located at the given URI
- * @throws IOException
- * When the URI cannot be loaded
- */
- public static EObject loadEMFModel(ResourceSet resourceSet, final URI uri) throws IOException {
- assert resourceSet != null : "null resourceSet"; //$NON-NLS-1$
- if (resourceSet == null) {
- Activator.log.warn("Created a new resourceSet to load an EMF model in " + Activator.log.getCallerMethod()); //$NON-NLS-1$
- resourceSet = new ResourceSetImpl();
- }
-
- try {
- Resource resource = resourceSet.getResource(uri, true);
- if (resource != null) {
- if (!resource.getContents().isEmpty()) {
- return resource.getContents().get(0);
- }
- }
- } catch (Exception ex) {
- IOException exception = new IOException(ex.toString());
- exception.initCause(ex);
- throw exception;
- }
-
- return null;
- }
-
- /**
- * Completely unloads a resource set so that it and all the models it contained may be reclaimed by the
- * Java garbage collector. This includes, at least:
- * <ul>
- * <li>unloading all resources in the set, which converts all model elements to proxies and removes all adapters from them</li>
- * <li>removing all resources from the set</li>
- * <li>removing all adapters from all resources</li>
- * <li>removing all adapters from the resource set</li>
- * </ul>
- *
- * @param resourceSet
- * the resource set to purge
- */
- public static void unload(ResourceSet resourceSet) {
- List<Resource> resources = ImmutableList.copyOf(resourceSet.getResources());
- resourceSet.getResources().clear();
- for (Resource next : resources) {
- next.unload();
- next.eAdapters().clear();
- }
- resourceSet.eAdapters().clear();
-
- // Clear the package registry (it may contain dynamic profile EPackages that we don't
- // want to leak in BasicExtendedMetaData instances attached to static EPackages)
- // Works around EMF bug 433108
- EPackage.Registry packageRegistry = resourceSet.getPackageRegistry();
- if (packageRegistry != null) {
- packageRegistry.clear();
- }
- }
-
- /**
- * Return the root package containing the given package, or the package
- * itself if it is already the root
- *
- * @param ePackage
- * @return
- * The Root package
- */
- public static EPackage getRootPackage(final EPackage ePackage) {
- if (ePackage == null) {
- return null;
- }
-
- if (ePackage.getESuperPackage() == null) {
- return ePackage;
- }
- return getRootPackage(ePackage.getESuperPackage());
- }
-
- /**
- * Gets the object of a given {@code type} containing an {@code object}, or the
- * {@code object} itself if it is of that {@code type}.
- *
- * @param object
- * the object for which to search for a container
- * @param type
- * the type of container to find
- *
- * @return the container of the requested {@code type}, or {@code null} if none
- */
- public static <T extends EObject> T getContainer(EObject object, EClass type) {
- T result = null;
-
- for (EObject next = object; (next != null) && (result == null); next = next.eContainer()) {
- if (type.isInstance(next)) {
- @SuppressWarnings("unchecked")
- T nextAsT = (T) next;
- result = nextAsT;
- }
- }
-
- return result;
- }
-
- /**
- * Gets the object of a given {@code type} containing an {@code object}, or the
- * {@code object} itself if it is of that {@code type}.
- *
- * @param object
- * the object for which to search for a container
- * @param type
- * the type of container to find
- *
- * @return the container of the requested {@code type}, or {@code null} if none
- */
- public static <T extends EObject> T getContainer(EObject object, Class<T> type) {
- T result = null;
-
- for (EObject next = object; (next != null) && (result == null); next = next.eContainer()) {
- if (type.isInstance(next)) {
- result = type.cast(next);
- }
- }
-
- return result;
- }
-
- /**
- * Return the list of EClasses that are subtypes
- * of the given EClass
- *
- * @param type
- * @param concreteClassesOnly
- * If true, only Concrete EClasses will be returned. Abstract and Interface EClasses will be filtered
- * @return
- * The list of EClasses implementing or extending the given EClass
- */
- public static List<EClass> getSubclassesOf(final EClass type, final boolean concreteClassesOnly) {
- Set<EClass> result = new LinkedHashSet<EClass>();
- if (!concreteClassesOnly || (!type.isAbstract() && !type.isInterface())) {
- result.add(type);
- }
-
- EPackage ePackage = getRootPackage(type.getEPackage());
- getSubclassesOf(type, ePackage, result, concreteClassesOnly);
- return new LinkedList<EClass>(result);
- }
-
- /**
- * Return the list of EClasses that are sub types
- * of the given EClass
- *
- * @param type
- * @param concreteClassesOnly
- * If true, only Concrete EClasses will be returned. Abstract and Interface EClasses will be filtered
- * @param packagesToBrowse
- * The EPackages in which the EClasses should be retrieved
- * @return
- * The list of EClasses implementing or extending the given EClass
- */
- public static List<EClass> getSubclassesOf(final EClass type, final boolean concreteClassesOnly, Collection<EPackage> packagesToBrowse) {
- Set<EClass> result = new LinkedHashSet<EClass>();
- if (!concreteClassesOnly || (!type.isAbstract() && !type.isInterface())) {
- result.add(type);
- }
-
- for (EPackage ePackage : packagesToBrowse) {
- getSubclassesOf(type, ePackage, result, concreteClassesOnly);
- }
-
- return new LinkedList<EClass>(result);
- }
-
- /**
- * Return the list of EClasses that are sub types of the given EClass
- *
- * @param type
- * @param concreteClassesOnly
- * If true, only Concrete EClasses will be returned. Abstract and Interface EClasses will be filtered
- * @param browseAllRegisteredPackages
- * If true, all registered EPackages will be navigated to retrieve the matching EClasses. Otherwise,
- * only the current EPackage will be used.
- * @return
- * The list of EClasses implementing or extending the given EClass
- */
- public static List<EClass> getSubclassesOf(final EClass type, final boolean concreteClassesOnly, final boolean browseAllRegisteredPackages) {
- // If the current package is a dynamic package, it may not be registered (?). Add it directly
- EPackage currentPackage = getRootPackage(type.getEPackage());
-
- Set<EPackage> allPackages = new LinkedHashSet<EPackage>();
- allPackages.add(currentPackage);
-
- if (browseAllRegisteredPackages) {
- // FIXME // WARNING: This loop will load all EPackages. The first call is expensive.
- Set<String> allUris = new HashSet<String>(EPackage.Registry.INSTANCE.keySet());
-
- for (String nsURI : allUris) {
- allPackages.add(EPackage.Registry.INSTANCE.getEPackage(nsURI));
- }
- }
-
- return getSubclassesOf(type, concreteClassesOnly, allPackages);
- }
-
- private static void getSubclassesOf(final EClass type, final EPackage fromPackage, final Set<EClass> result, final boolean concreteClassesOnly) {
- for (EClassifier classifier : fromPackage.getEClassifiers()) {
- if (classifier instanceof EClass) {
- EClass eClass = (EClass) classifier;
- if (eClass.getEAllSuperTypes().contains(type)) {
- if (!concreteClassesOnly || (!eClass.isAbstract() && !eClass.isInterface())) {
- result.add(eClass);
- }
- }
- }
- }
-
- for (EPackage subPackage : fromPackage.getESubpackages()) {
- getSubclassesOf(type, subPackage, result, concreteClassesOnly);
- }
- }
-
- /**
- * Tests if an EObject is read only on any {@linkplain ReadOnlyAxis axis}.
- * Delegates to the EObject's editing domain if it can be found
- *
- * @param eObject
- * @return
- * True if the EObject is read only on any axis
- * @see #isReadOnly(Set, EObject, EditingDomain)
- */
- public static boolean isReadOnly(final EObject eObject) {
- return isReadOnly(ReadOnlyAxis.anyAxis(), eObject);
- }
-
- /**
- * Tests if an EObject is read only on any of the specified {@code axes}.
- * Delegates to the EObject's editing domain if it can be found
- *
- * @param axes
- * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful
- * @param eObject
- * @return
- * True if the EObject is read only on any of the given {@code axes}
- */
- public static boolean isReadOnly(Set<ReadOnlyAxis> axes, final EObject eObject) {
- EditingDomain domain = resolveEditingDomain(eObject);
- return isReadOnly(axes, eObject, domain);
- }
-
- /**
- * Tests if an EObject is read only on any {@linkplain ReadOnlyAxis axis}.
- * Delegates to the given editing domain if it isn't null
- *
- * @param eObject
- * @param domain
- * @return
- * True if the EObject is read only on any axis
- */
- public static boolean isReadOnly(final EObject eObject, final EditingDomain domain) {
- return isReadOnly(ReadOnlyAxis.anyAxis(), eObject, domain);
- }
-
- /**
- * Tests if an EObject is read only on any of the specified {@code axes}.
- * Delegates to the given editing domain if it isn't null
- *
- * @param axes
- * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful
- * @param eObject
- *
- * @param domain
- * @return
- * True if the EObject is read only
- */
- public static boolean isReadOnly(Set<ReadOnlyAxis> axes, final EObject eObject, final EditingDomain domain) {
- if (domain != null) {
- Object handler = PlatformHelper.getAdapter(domain, IReadOnlyHandler.class);
- if (handler instanceof IReadOnlyHandler2) {
- return ((IReadOnlyHandler2) handler).isReadOnly(axes, eObject).get();
- } else if (handler instanceof IReadOnlyHandler) {
- // these handlers only deal with permission-based read-only-ness
- return axes.contains(ReadOnlyAxis.PERMISSION) && ((IReadOnlyHandler) handler).isReadOnly(eObject).get();
- }
-
- if (eObject.eResource() != null) {
- return domain.isReadOnly(eObject.eResource());
- }
- }
- return false;
- }
-
- /**
- * Tests if the Resource is read only on any {@linkplain ReadOnlyAxis axis}.
- * Delegates to the given editing domain if it isn't null
- *
- * @param resource
- * @param domain
- * @return
- * True if the Resource is read only on any axis
- */
- public static boolean isReadOnly(final Resource resource, final EditingDomain domain) {
- return isReadOnly(ReadOnlyAxis.anyAxis(), resource, domain);
- }
-
- /**
- * Tests if the Resource is read only on any of the given {@code axes}.
- * Delegates to the given editing domain if it isn't null
- *
- * @param axes
- * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful
- * @param resource
- * @param domain
- * @return
- * True if the Resource is read only on any of the given {@code axes}
- */
- public static boolean isReadOnly(Set<ReadOnlyAxis> axes, final Resource resource, final EditingDomain domain) {
- if (resource == null) {
- return false;
- }
-
- if (domain != null && resource.getURI() != null) {
- Object handler = PlatformHelper.getAdapter(domain, IReadOnlyHandler.class);
- if (handler instanceof IReadOnlyHandler2) {
- return ((IReadOnlyHandler2) handler).anyReadOnly(axes, new URI[] { resource.getURI() }).get();
- } else if (handler instanceof IReadOnlyHandler) {
- // these handlers only deal with permission-based read-only-ness
- return axes.contains(ReadOnlyAxis.PERMISSION) && ((IReadOnlyHandler) handler).anyReadOnly(new URI[] { resource.getURI() }).get();
- }
- return domain.isReadOnly(resource);
- }
-
- // no editing domain : use file system attribute
- ResourceSet resourceSet = resource.getResourceSet();
-
- if (resourceSet == null) {
- return false;
- }
-
- Map<String, ?> attributes = resourceSet.getURIConverter().getAttributes(resource.getURI(), null);
- Boolean readOnly = (Boolean) attributes.get(URIConverter.ATTRIBUTE_READ_ONLY);
-
- return readOnly == null ? false : readOnly;
- }
-
- /**
- * Tests if an object that is read only could possibly be made writable by some means (file system attributes, team provider hook, database
- * permissions, etc.)
- *
- * @param eObject
- * an object that is assumed to be read-only
- * @param domain
- * the editing domain context of the {@link eObject}
- * @return
- * whether the {@code eObject} could be made writable
- */
- public static boolean canMakeWritable(final EObject eObject, final EditingDomain domain) {
- return canMakeWritable(ReadOnlyAxis.anyAxis(), eObject, domain);
- }
-
- /**
- * Tests if an object that is read only could possibly be made writable according to any of
- * the specified {@code axes} of read-only-ness.
- *
- * @param axes
- * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful
- * @param eObject
- * an object that is assumed to be read-only
- * @param domain
- * the editing domain context of the {@link eObject}
- * @return
- * whether the {@code eObject} could be made writable
- */
- public static boolean canMakeWritable(Set<ReadOnlyAxis> axes, final EObject eObject, final EditingDomain domain) {
- if (domain != null) {
- Object handler = PlatformHelper.getAdapter(domain, IReadOnlyHandler.class);
- if (handler instanceof IReadOnlyHandler2) {
- return ((IReadOnlyHandler2) handler).canMakeWritable(axes, eObject).or(false);
- }
- }
- return false;
- }
-
- /**
- * Tests if a resource that is read only could possibly be made writable by some means (file system attributes, team provider hook, database
- * permissions, etc.)
- *
- * @param resource
- * a resource that is assumed to be read-only
- * @param domain
- * the editing domain context of the {@link resource}
- * @return
- * whether the {@code resource} could be made writable
- */
- public static boolean canMakeWritable(final Resource resource, final EditingDomain domain) {
- return canMakeWritable(ReadOnlyAxis.anyAxis(), resource, domain);
- }
-
- /**
- * Tests if a resource that is read only could possibly be made writable according to any of
- * the specified {@code axes} of read-only-ness.
- *
- * @param axes
- * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful
- * @param resource
- * a resource that is assumed to be read-only
- * @param domain
- * the editing domain context of the {@link resource}
- * @return
- * whether the {@code resource} could be made writable
- */
- public static boolean canMakeWritable(Set<ReadOnlyAxis> axes, final Resource resource, final EditingDomain domain) {
- if (domain != null) {
- Object handler = PlatformHelper.getAdapter(domain, IReadOnlyHandler.class);
- if (handler instanceof IReadOnlyHandler2) {
- return ((IReadOnlyHandler2) handler).canMakeWritable(axes, new URI[] { resource.getURI() }).or(false);
- }
- }
- return false;
- }
-
- /**
- * Tests if the given EStructuralFeature is required (ie. should always
- * have a value)
- *
- * A feature is required if at least of one the following conditions if
- * true :
- *
- * - It has a defaultValue
- * - Its lowerBound is at least 1
- * - It is an enumeration (Enumerations always have a default value)
- * - It is a Java primitive type, and is not marked as Unsettable
- *
- * @param feature
- * the feature to test
- * @return
- * true if the feature is required, false otherwise
- */
- public static boolean isRequired(final EStructuralFeature feature) {
- EClassifier eType = feature.getEType();
- if (eType == null) {
- return false;
- }
-
- // EEnums are always required, as an EEnum always has a default value
- if (eType instanceof EEnum) {
- return true;
- }
-
- // At least one value means it is required
- if (feature.getLowerBound() >= 1) {
- return true;
- }
-
- // Java primitive types cannot have a null value
- // if the feature is not specifically marked as unsettable, then it is required
- if (eType.getInstanceClass() != null && eType.getInstanceClass().isPrimitive() && !feature.isUnsettable()) {
- return true;
- }
-
- // If there is a default value, there is always a value
- if (feature.getDefaultValueLiteral() != null) {
- return true;
- }
-
- return false; // The property if not required
- }
-
- /**
- * Returns all objects of type T contained in the resource
- *
- * @param resource
- * @param type
- * @return
- */
- public static <T> Set<T> allInstances(final Resource resource, Class<T> type) {
- TreeIterator<EObject> iterator = resource.getAllContents();
- Set<T> result = new LinkedHashSet<T>();
-
- while (iterator.hasNext()) {
- EObject element = iterator.next();
- if (type.isInstance(element)) {
- result.add(type.cast(element));
- }
- }
-
- return result;
- }
-
- /**
- * Returns all the EPackages and nested EPackages contained in this resource
- *
- * @param resource
- * @return
- */
- public static Set<EPackage> getAllEPackages(final Resource resource) {
- Set<EPackage> result = new LinkedHashSet<EPackage>();
-
- for (EObject rootElement : resource.getContents()) {
- if (rootElement instanceof EPackage) {
- result.add((EPackage) rootElement);
- result.addAll(getAllNestedPackages((EPackage) rootElement));
- }
- }
-
- return result;
- }
-
- /**
- * Returns all packages nested in the given EPackage (recursively). Does not
- * include the base EPackage.
- *
- * @param basePackage
- * @return
- */
- public static Set<EPackage> getAllNestedPackages(EPackage basePackage) {
- Set<EPackage> result = new LinkedHashSet<EPackage>();
-
- for (EPackage nestedPackage : basePackage.getESubpackages()) {
- result.add(nestedPackage);
- result.addAll(getAllNestedPackages(nestedPackage));
- }
-
- return result;
- }
-
- /**
- *
- * @param resource
- * a resource
- *
- * @return
- * the list of the metamodels known by the resource
- */
- public static Set<EPackage> getMetamodels(final Resource resource) {
- Set<EPackage> metamodels = new HashSet<EPackage>();
- if (resource != null) {
- final List<EObject> contents = new ArrayList<EObject>(resource.getContents());
- for (final EObject current : contents) {
- metamodels.add(current.eClass().getEPackage());
- }
- }
- return metamodels;
- }
-
- /**
- *
- * Returns the XMI ID of the given {@link EObject} or <code>null</code> if it cannot be resolved.
- *
- * @param object
- * Object which we seek the XMI ID of.
- * @return <code>object</code>'s XMI ID, <code>null</code> if not applicable.
- */
- public static final String getXMIID(final EObject object) {
- String objectID = null;
- if (object != null && object.eResource() instanceof XMIResource) {
- objectID = ((XMIResource) object.eResource()).getID(object);
- }
- return objectID;
- }
-
-
-
- /**
- * Gets the usages.
- *
- * @param source
- * the source
- *
- * @return the usages or null if there is no usages
- */
- public static Collection<Setting> getUsages(EObject source) {
- // the functional code is defined in core because we need it in infra.core
- // but infra.core can't depend on infra.emf (circular dependency)
- return org.eclipse.papyrus.infra.core.utils.EMFHelper.getUsages(source);
- }
-
-
- /**
- * <pre>
- * Test if the used element is referenced by other elements than the known
- * referencer (except its container). It ignores references from an other meta-model.
- * </pre>
- *
- * @param usedObject
- * the used object
- * @param knownReferencer
- * the known referencer
- * @return true if the known referencer is the only referencer.
- */
- public static boolean isOnlyUsage(EObject usedObject, EObject knownReferencer) {
- boolean isUsed = false;
- EPackage mmPackage = usedObject.eClass().getEPackage();
-
- // Retrieve the list of elements referencing the usedObject.
- Set<EObject> crossReferences = new HashSet<EObject>();
- for (Setting setting : getUsages(usedObject)) {
- EObject eObj = setting.getEObject();
- if (eObj.eClass().getEPackage().equals(mmPackage)) {
- crossReferences.add(eObj);
- }
- }
-
- // Remove the container of used object.
- crossReferences.remove(usedObject.eContainer());
- // Remove the knownReferencer from the list of references.
- crossReferences.remove(knownReferencer);
-
- // If no referencer remains in the list, the known element is the only
- // usage.
- if (crossReferences.isEmpty()) {
- isUsed = true;
- }
-
- return isUsed;
- }
-
- /**
- *
- * @param superType
- * an eclassifier
- * @param subType
- * another eClassifier
- * @return
- * <code>true</code> if the 2nd {@link EClassifier} is a subtype of the first one
- */
- public static boolean isSuperType(final EClassifier superType, final EClassifier subType) {
- if (superType == subType) {
- return true;
- }
-
- if (superType instanceof EClass && subType instanceof EClass) {
- // special case because isSuperTypeOf doesn't handle it
- if (superType == EcorePackage.eINSTANCE.getEObject()) {
- return true;
- }
-
- EClass superTypeEClass = (EClass) superType;
- EClass subTypeEClass = (EClass) subType;
- return superTypeEClass.isSuperTypeOf(subTypeEClass);
- }
-
- // manage EDtataType
- if (superType == EcorePackage.eINSTANCE.getEDataType() && subType instanceof EDataType) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Computes the path from the root EObject to the given element, as a List of EObjects
- *
- * @param element
- * @return
- */
- public static List<EObject> getContainmentPath(EObject element) {
- List<EObject> result;
- if (element.eContainer() == null) {
- result = new LinkedList<EObject>();
- result.add(element);
- return result;
- } else {
- result = getContainmentPath(element.eContainer());
- result.add(element);
- }
- return result;
- }
-
- /**
- * Returns the given element, reloaded into the resource set of the context element,
- * or the source element itself if not possible.
- *
- * Use this method for e.g. loading an element from a shared resource set into another resource set
- * (Apply a registered profile/library, drop an element from the project explorer, ...)
- *
- * @param element
- * @param contextElement
- * @return
- */
- public static <T extends EObject> T reloadIntoContext(T element, EObject contextElement) {
- ResourceSet sourceResourceSet = getResourceSet(element);
- ResourceSet loadingContext = getResourceSet(contextElement);
-
- if (sourceResourceSet == loadingContext || loadingContext == null) {
- return element;
- }
-
- URI sourceURI = EcoreUtil.getURI(element);
- EObject result = loadingContext.getEObject(sourceURI, true);
-
- return (T) result;
- }
-
- /**
- * Returns the resourceSet owning this eObject, or null if it is detached
- *
- * @param eObject
- */
- public static ResourceSet getResourceSet(EObject eObject) {
- Resource resource = (eObject == null) ? null : eObject.eResource();
- return (resource == null) ? null : resource.getResourceSet();
- }
-
- /**
- * Best-effort loads a resource, returning the first root element of the requested {@code type}. Unlike the {@link #loadChecked(ResourceSet, URI, Class) loadChecked} method, this will never throw an exception.
- *
- * @param rset
- * a resource set in which to load the resource
- * @param uri
- * the URI of the resource to load
- * @param type
- * the type of root element to retrieve
- *
- * @return the requested root element, or {@code null} if the resource does not contain such an element or could not be loaded
- *
- * @see #loadChecked(ResourceSet, URI, Class)
- */
- public static <T extends EObject> T load(ResourceSet rset, URI uri, Class<T> type) {
- T result = null;
-
- try {
- result = loadChecked(rset, uri, type);
- } catch (Exception e) {
- Activator.log.error("Exception in loading resource " + uri, e); //$NON-NLS-1$
-
- // Maybe it was partially loaded? If so, try again
- Resource res = rset.getResource(uri, false);
- if ((res != null) && res.isLoaded()) {
- result = Iterables.getFirst(Iterables.filter(res.getContents(), type), null);
- }
- }
-
- return result;
- }
-
- /**
- * Best-effort loads a resource, returning the first root element of the requested {@code type}.
- *
- * @param rset
- * a resource set in which to load the resource
- * @param uri
- * the URI of the resource to load
- * @param type
- * the type of root element to retrieve
- *
- * @return the requested root element, or {@code null} if the resource does not contain such an element or could not be loaded
- * @throws IOException
- * on an I/O problem in loading the resource
- * @throw RuntimeException on any other unforeseen (usually programming error) problem
- *
- * @see #load(ResourceSet, URI, Class)
- */
- public static <T extends EObject> T loadChecked(ResourceSet rset, URI uri, Class<T> type) throws IOException {
- try {
- return Iterables.getFirst(Iterables.filter(rset.getResource(uri, true).getContents(), type), null);
- } catch (WrappedException e) {
- if (e.exception() instanceof IOException) {
- throw (IOException) e.exception();
- } else if (e.exception() instanceof RuntimeException) {
- throw (RuntimeException) e.exception();
- } else {
- throw e;
- }
- }
- }
-}
+/***************************************************************************** + * Copyright (c) 2010, 2016 CEA LIST, Christian W. Damus, and others. + * + * 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 + * Christian W. Damus (CEA) - filter out EObjects that are Resources (CDO) + * Christian W. Damus (CEA) - Support read-only state at object level (CDO) + * Christian W. Damus (CEA) - bugs 323802, 429826, 408491, 432813, 422257 + * Christian W. Damus - bugs 469188, 485220 + * + *****************************************************************************/ +package org.eclipse.papyrus.infra.emf.utils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.Platform; +import org.eclipse.emf.common.util.TreeIterator; +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.common.util.WrappedException; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EClassifier; +import org.eclipse.emf.ecore.EDataType; +import org.eclipse.emf.ecore.EEnum; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EPackage; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.EStructuralFeature.Setting; +import org.eclipse.emf.ecore.EcorePackage; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.resource.URIConverter; +import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.ecore.xmi.XMIResource; +import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.papyrus.infra.core.resource.IReadOnlyHandler; +import org.eclipse.papyrus.infra.core.resource.IReadOnlyHandler2; +import org.eclipse.papyrus.infra.core.resource.ReadOnlyAxis; +import org.eclipse.papyrus.infra.core.services.ServiceException; +import org.eclipse.papyrus.infra.core.utils.ServiceUtils; +import org.eclipse.papyrus.infra.emf.Activator; +import org.eclipse.papyrus.infra.tools.util.PlatformHelper; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; + +/** + * A Helper class for manipulating EMF Objects + * + * @author Camille Letavernier + */ +// TODO : Check implementations. Most of them are old and don't always match the specification +public class EMFHelper { + + /** + * Returns the EClass corresponding to the given nsUri and className + * + * @param nsUri + * The NSURI of the EClass' EPackage + * @param className + * The EClass' name + * @return + * The EClass instance, or null if the EClass couldn't be found + */ + public static EClass getEClass(final String nsUri, final String className) { + EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsUri); + if (ePackage == null) { + Activator.log.warn("Cannot find an EPackage matching the nsURI " + nsUri); //$NON-NLS-1$ + return null; + } + return getEClass(ePackage, className); + } + + /** + * Return the EClass corresponding to the given EPackage and className + * + * @param metamodel + * The EClass' EPackage + * @param className + * The EClass' name + * @return + * The EClass instance, or null if the EClass couldn't be found + */ + public static EClass getEClass(final EPackage metamodel, final String className) { + EClassifier classifier = metamodel.getEClassifier(className); + if (classifier == null) { + Activator.log.warn("Classifier " + className + " not found in metamodel " + metamodel.getName() + " (" + metamodel.getNsURI() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + if (classifier instanceof EClass) { + return (EClass) classifier; + } else { + Activator.log.warn("Classifier " + className + " in " + metamodel.getName() + " (" + metamodel.getNsURI() + ") is not an EClass"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + return null; + } + + /** + * Tests if an Object is an instance of the given EClass + * + * @param element + * The EObject to test + * @param className + * The name of the EClass + * @param metamodel + * The EPackage owning the EClass + * @return + * True if the EObject is an instance of the EClass, or of one of the EClass' subtypes + */ + public static boolean isInstance(final EObject element, final String className, final EPackage metamodel) { + + EClassifier theClass = metamodel.getEClassifier(className); + + if (theClass == null) { + String message = String.format("Class %s not found in metamodel: %s (%s)", className, metamodel.getName(), metamodel.getNsURI());//$NON-NLS-1$ + Activator.log.warn(message); + return false; + } + + return theClass.isInstance(element); + } + + + /** + * Tests if an Object is an instance of the given EClass + * + * @param element + * The EObject to test + * @param className + * The name of the EClass + * @param metamodel + * The URI of the EPackage owning the EClass + * @return + * True if the EObject is an instance of the EClass, or of one of the EClass' subtypes + */ + public static boolean isInstance(EObject selectedItem, String className, String nsUri) { + EClass actualEClass = selectedItem.eClass(); + + // Exact match + if (isExactMatch(actualEClass, className, nsUri)) { + return true; + } + + List<EClass> allSuperTypes = actualEClass.getEAllSuperTypes(); + for (EClass eClass : allSuperTypes) { + if (isExactMatch(eClass, className, nsUri)) { + return true; + } + } + + return false; + } + + /** + * Tests whether the given EClass has the given className and belongs to the EPackage represented by nsUri + * + * @param eClass + * The EClass to test + * @param className + * + * @param nsUri + * @return + * True if the EClass' name is className and the EClass' EPackage's nsURI is nsUri + */ + private static boolean isExactMatch(EClass eClass, String className, String nsUri) { + return className.equals(eClass.getName()) && nsUri.equals(eClass.getEPackage().getNsURI()); + } + + /** + * Tests if the given eClass is a Subclass of fromClass + * Also returns true when eClass == fromClass + * + * @param eClass + * @param fromClass + * @return + * true if eClass is a subclass of fromClass + */ + public static boolean isSubclass(final EClass eClass, final EClass fromClass) { + // Everything is an EObject + if (eClass != null && fromClass == EcorePackage.eINSTANCE.getEObject()) { + return true; + } + + if (eClass == fromClass) { + return true; + } + + List<EClass> superTypes = eClass.getEAllSuperTypes(); + if (superTypes.contains(fromClass)) { + return true; + } + + return false; + } + + /** + * Returns the EObject corresponding to the input object + * Tests if the input is an EObject, or if it is Adaptable + * to an EObject + * + * @param source + * @return An EObject corresponding to the input source, or null + * if the EObject could not be resolved + */ + public static EObject getEObject(final Object source) { + + // Support for EMF 0.2 CustomizedTree: The TreeElements are IAdaptable, but the + // EObject adapter for some of them (e.g. reference node) is not what we need + Object resolved = Activator.getDefault().getEObjectResolver().resolve(source); + if ((resolved != source) && isEMFModelElement(resolved)) { + return (EObject) resolved; + } + + // General case + if (isEMFModelElement(source)) { + return (EObject) source; + } + + // Try to get an intrinsic adapter + if (source instanceof IAdaptable) { + EObject eObject = ((IAdaptable) source).getAdapter(EObject.class); + if (eObject == null) { // EMF Facet 0.1 + eObject = ((IAdaptable) source).getAdapter(EReference.class); + } + + if (eObject != null) { + return asEMFModelElement(eObject); // in case the adapter is a CDOResource + } + } + + // External adapter (last ditch case) + if (source != null) { + return asEMFModelElement(Platform.getAdapterManager().getAdapter(source, EObject.class)); + } + + return null; + } + + /** + * Queries whether an {@code object} is an EMF model element, an instance of + * some {@link EClass} from an EMF model. This isn't as simple as checking + * whether the object is an {@link EObject} because there are edge cases + * where objects are {@code EObject}s but shouldn't be treated as + * "model content". But, a minimum requirement is that the {@code object} is + * an {@link EObject}. + * + * @param object + * an object + * @return whether it is "model content" + * + * @see EMFHelper#asEMFModelElement(Object) + */ + public static boolean isEMFModelElement(Object object) { + return (object instanceof EObject) && !(object instanceof Resource); + } + + /** + * Casts an {@code object} as an EMF model element, if appropriate. + * + * @param object + * an object + * @return the object as an EMF model element, or {@code null} if it is not + * an EMF model element + * + * @see #isEMFModelElement(Object) + */ + public static EObject asEMFModelElement(Object object) { + return isEMFModelElement(object) ? (EObject) object : null; + } + + /** + * Retrieve the EditingDomain for the given source object. The object is first + * resolved to an EObject through #getEObject when possible. + * + * @param source + * @return + * The source object's editing domain, or null if it couldn't be found + */ + public static EditingDomain resolveEditingDomain(final Object source) { + return resolveEditingDomain(getEObject(source)); + } + + /** + * Retrieve the EditingDomain for the given source EObject + * + * @param source + * @return + * The source eObject's editing domain, or null if it couldn't be found + */ + public static EditingDomain resolveEditingDomain(final EObject source) { + EditingDomain domain = AdapterFactoryEditingDomain.getEditingDomainFor(source); + if (domain == null) { + try { + domain = ServiceUtils.getInstance().getTransactionalEditingDomain(null); + } catch (ServiceException e) { + // Ignore: We cannot find the domain + } + } + return domain; + } + + /** + * Return the eClassifier' qualified name. The qualified name is obtained by the concatenation + * of its package hierarchy with the class name, separated by the given separator + * + * @param eClassifier + * @param separator + * The separator used between each package name + * @return + * The EClassifier' qualified name + */ + public static String getQualifiedName(final EClassifier eClassifier, final String separator) { + return getQualifiedName(eClassifier.getEPackage(), separator) + separator + eClassifier.getName(); + } + + /** + * Return the ePackage's qualified name. The qualified name is obtained by the concatenation + * of its superPackage hierarchy with the ePackage name, separated by the given separator + * + * @param ePackage + * @param separator + * The separator used between each package name + * @return + * The EPackage's qualified name + */ + public static String getQualifiedName(final EPackage ePackage, final String separator) { + if (ePackage.getESuperPackage() == null) { + return ePackage.getName(); + } + return getQualifiedName(ePackage.getESuperPackage(), separator) + separator + ePackage.getName(); + } + + + /** + * Loads and returns the first EObject at the given URI. + * The EObject is loaded in the given resourceSet. + * + * @param resourceSet + * The ResourceSet in which the model will be loaded + * @param uri + * The URI describing the location of the model to load + * @return + * The first EObject located at the given URI + * @throws IOException + * When the URI cannot be loaded + */ + public static EObject loadEMFModel(ResourceSet resourceSet, final URI uri) throws IOException { + assert resourceSet != null : "null resourceSet"; //$NON-NLS-1$ + if (resourceSet == null) { + Activator.log.warn("Created a new resourceSet to load an EMF model in " + Activator.log.getCallerMethod()); //$NON-NLS-1$ + resourceSet = new ResourceSetImpl(); + } + + try { + Resource resource = resourceSet.getResource(uri, true); + if (resource != null) { + if (!resource.getContents().isEmpty()) { + return resource.getContents().get(0); + } + } + } catch (Exception ex) { + IOException exception = new IOException(ex.toString()); + exception.initCause(ex); + throw exception; + } + + return null; + } + + /** + * Completely unloads a resource set so that it and all the models it contained may be reclaimed by the + * Java garbage collector. This includes, at least: + * <ul> + * <li>unloading all resources in the set, which converts all model elements to proxies and removes all adapters from them</li> + * <li>removing all resources from the set</li> + * <li>removing all adapters from all resources</li> + * <li>removing all adapters from the resource set</li> + * </ul> + * + * @param resourceSet + * the resource set to purge + */ + public static void unload(ResourceSet resourceSet) { + List<Resource> resources = ImmutableList.copyOf(resourceSet.getResources()); + resourceSet.getResources().clear(); + for (Resource next : resources) { + next.unload(); + next.eAdapters().clear(); + } + resourceSet.eAdapters().clear(); + + // Clear the package registry (it may contain dynamic profile EPackages that we don't + // want to leak in BasicExtendedMetaData instances attached to static EPackages) + // Works around EMF bug 433108 + EPackage.Registry packageRegistry = resourceSet.getPackageRegistry(); + if (packageRegistry != null) { + packageRegistry.clear(); + } + } + + /** + * Return the root package containing the given package, or the package + * itself if it is already the root + * + * @param ePackage + * @return + * The Root package + */ + public static EPackage getRootPackage(final EPackage ePackage) { + if (ePackage == null) { + return null; + } + + if (ePackage.getESuperPackage() == null) { + return ePackage; + } + return getRootPackage(ePackage.getESuperPackage()); + } + + /** + * Gets the object of a given {@code type} containing an {@code object}, or the + * {@code object} itself if it is of that {@code type}. + * + * @param object + * the object for which to search for a container + * @param type + * the type of container to find + * + * @return the container of the requested {@code type}, or {@code null} if none + * @since 2.0 + */ + public static <T extends EObject> T getContainer(EObject object, EClass type) { + T result = null; + + for (EObject next = object; (next != null) && (result == null); next = next.eContainer()) { + if (type.isInstance(next)) { + @SuppressWarnings("unchecked") + T nextAsT = (T) next; + result = nextAsT; + } + } + + return result; + } + + /** + * Gets the object of a given {@code type} containing an {@code object}, or the + * {@code object} itself if it is of that {@code type}. + * + * @param object + * the object for which to search for a container + * @param type + * the type of container to find + * + * @return the container of the requested {@code type}, or {@code null} if none + * @since 2.0 + */ + public static <T extends EObject> T getContainer(EObject object, Class<T> type) { + T result = null; + + for (EObject next = object; (next != null) && (result == null); next = next.eContainer()) { + if (type.isInstance(next)) { + result = type.cast(next); + } + } + + return result; + } + + /** + * Return the list of EClasses that are subtypes + * of the given EClass + * + * @param type + * @param concreteClassesOnly + * If true, only Concrete EClasses will be returned. Abstract and Interface EClasses will be filtered + * @return + * The list of EClasses implementing or extending the given EClass + */ + public static List<EClass> getSubclassesOf(final EClass type, final boolean concreteClassesOnly) { + Set<EClass> result = new LinkedHashSet<EClass>(); + if (!concreteClassesOnly || (!type.isAbstract() && !type.isInterface())) { + result.add(type); + } + + EPackage ePackage = getRootPackage(type.getEPackage()); + getSubclassesOf(type, ePackage, result, concreteClassesOnly); + return new LinkedList<EClass>(result); + } + + /** + * Return the list of EClasses that are sub types + * of the given EClass + * + * @param type + * @param concreteClassesOnly + * If true, only Concrete EClasses will be returned. Abstract and Interface EClasses will be filtered + * @param packagesToBrowse + * The EPackages in which the EClasses should be retrieved + * @return + * The list of EClasses implementing or extending the given EClass + */ + public static List<EClass> getSubclassesOf(final EClass type, final boolean concreteClassesOnly, Collection<EPackage> packagesToBrowse) { + Set<EClass> result = new LinkedHashSet<EClass>(); + if (!concreteClassesOnly || (!type.isAbstract() && !type.isInterface())) { + result.add(type); + } + + for (EPackage ePackage : packagesToBrowse) { + getSubclassesOf(type, ePackage, result, concreteClassesOnly); + } + + return new LinkedList<EClass>(result); + } + + /** + * Return the list of EClasses that are sub types of the given EClass + * + * @param type + * @param concreteClassesOnly + * If true, only Concrete EClasses will be returned. Abstract and Interface EClasses will be filtered + * @param browseAllRegisteredPackages + * If true, all registered EPackages will be navigated to retrieve the matching EClasses. Otherwise, + * only the current EPackage will be used. + * @return + * The list of EClasses implementing or extending the given EClass + */ + public static List<EClass> getSubclassesOf(final EClass type, final boolean concreteClassesOnly, final boolean browseAllRegisteredPackages) { + // If the current package is a dynamic package, it may not be registered (?). Add it directly + EPackage currentPackage = getRootPackage(type.getEPackage()); + + Set<EPackage> allPackages = new LinkedHashSet<EPackage>(); + allPackages.add(currentPackage); + + if (browseAllRegisteredPackages) { + // FIXME // WARNING: This loop will load all EPackages. The first call is expensive. + Set<String> allUris = new HashSet<String>(EPackage.Registry.INSTANCE.keySet()); + + for (String nsURI : allUris) { + allPackages.add(EPackage.Registry.INSTANCE.getEPackage(nsURI)); + } + } + + return getSubclassesOf(type, concreteClassesOnly, allPackages); + } + + private static void getSubclassesOf(final EClass type, final EPackage fromPackage, final Set<EClass> result, final boolean concreteClassesOnly) { + for (EClassifier classifier : fromPackage.getEClassifiers()) { + if (classifier instanceof EClass) { + EClass eClass = (EClass) classifier; + if (eClass.getEAllSuperTypes().contains(type)) { + if (!concreteClassesOnly || (!eClass.isAbstract() && !eClass.isInterface())) { + result.add(eClass); + } + } + } + } + + for (EPackage subPackage : fromPackage.getESubpackages()) { + getSubclassesOf(type, subPackage, result, concreteClassesOnly); + } + } + + /** + * Tests if an EObject is read only on any {@linkplain ReadOnlyAxis axis}. + * Delegates to the EObject's editing domain if it can be found + * + * @param eObject + * @return + * True if the EObject is read only on any axis + * @see #isReadOnly(Set, EObject, EditingDomain) + */ + public static boolean isReadOnly(final EObject eObject) { + return isReadOnly(ReadOnlyAxis.anyAxis(), eObject); + } + + /** + * Tests if an EObject is read only on any of the specified {@code axes}. + * Delegates to the EObject's editing domain if it can be found + * + * @param axes + * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful + * @param eObject + * @return + * True if the EObject is read only on any of the given {@code axes} + */ + public static boolean isReadOnly(Set<ReadOnlyAxis> axes, final EObject eObject) { + EditingDomain domain = resolveEditingDomain(eObject); + return isReadOnly(axes, eObject, domain); + } + + /** + * Tests if an EObject is read only on any {@linkplain ReadOnlyAxis axis}. + * Delegates to the given editing domain if it isn't null + * + * @param eObject + * @param domain + * @return + * True if the EObject is read only on any axis + */ + public static boolean isReadOnly(final EObject eObject, final EditingDomain domain) { + return isReadOnly(ReadOnlyAxis.anyAxis(), eObject, domain); + } + + /** + * Tests if an EObject is read only on any of the specified {@code axes}. + * Delegates to the given editing domain if it isn't null + * + * @param axes + * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful + * @param eObject + * + * @param domain + * @return + * True if the EObject is read only + */ + public static boolean isReadOnly(Set<ReadOnlyAxis> axes, final EObject eObject, final EditingDomain domain) { + if (domain != null) { + Object handler = PlatformHelper.getAdapter(domain, IReadOnlyHandler.class); + if (handler instanceof IReadOnlyHandler2) { + return ((IReadOnlyHandler2) handler).isReadOnly(axes, eObject).get(); + } else if (handler instanceof IReadOnlyHandler) { + // these handlers only deal with permission-based read-only-ness + return axes.contains(ReadOnlyAxis.PERMISSION) && ((IReadOnlyHandler) handler).isReadOnly(eObject).get(); + } + + if (eObject.eResource() != null) { + return domain.isReadOnly(eObject.eResource()); + } + } + return false; + } + + /** + * Tests if the Resource is read only on any {@linkplain ReadOnlyAxis axis}. + * Delegates to the given editing domain if it isn't null + * + * @param resource + * @param domain + * @return + * True if the Resource is read only on any axis + */ + public static boolean isReadOnly(final Resource resource, final EditingDomain domain) { + return isReadOnly(ReadOnlyAxis.anyAxis(), resource, domain); + } + + /** + * Tests if the Resource is read only on any of the given {@code axes}. + * Delegates to the given editing domain if it isn't null + * + * @param axes + * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful + * @param resource + * @param domain + * @return + * True if the Resource is read only on any of the given {@code axes} + */ + public static boolean isReadOnly(Set<ReadOnlyAxis> axes, final Resource resource, final EditingDomain domain) { + if (resource == null) { + return false; + } + + if (domain != null && resource.getURI() != null) { + Object handler = PlatformHelper.getAdapter(domain, IReadOnlyHandler.class); + if (handler instanceof IReadOnlyHandler2) { + return ((IReadOnlyHandler2) handler).anyReadOnly(axes, new URI[] { resource.getURI() }).get(); + } else if (handler instanceof IReadOnlyHandler) { + // these handlers only deal with permission-based read-only-ness + return axes.contains(ReadOnlyAxis.PERMISSION) && ((IReadOnlyHandler) handler).anyReadOnly(new URI[] { resource.getURI() }).get(); + } + return domain.isReadOnly(resource); + } + + // no editing domain : use file system attribute + ResourceSet resourceSet = resource.getResourceSet(); + + if (resourceSet == null) { + return false; + } + + Map<String, ?> attributes = resourceSet.getURIConverter().getAttributes(resource.getURI(), null); + Boolean readOnly = (Boolean) attributes.get(URIConverter.ATTRIBUTE_READ_ONLY); + + return readOnly == null ? false : readOnly; + } + + /** + * Tests if an object that is read only could possibly be made writable by some means (file system attributes, team provider hook, database + * permissions, etc.) + * + * @param eObject + * an object that is assumed to be read-only + * @param domain + * the editing domain context of the {@link eObject} + * @return + * whether the {@code eObject} could be made writable + */ + public static boolean canMakeWritable(final EObject eObject, final EditingDomain domain) { + return canMakeWritable(ReadOnlyAxis.anyAxis(), eObject, domain); + } + + /** + * Tests if an object that is read only could possibly be made writable according to any of + * the specified {@code axes} of read-only-ness. + * + * @param axes + * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful + * @param eObject + * an object that is assumed to be read-only + * @param domain + * the editing domain context of the {@link eObject} + * @return + * whether the {@code eObject} could be made writable + */ + public static boolean canMakeWritable(Set<ReadOnlyAxis> axes, final EObject eObject, final EditingDomain domain) { + if (domain != null) { + Object handler = PlatformHelper.getAdapter(domain, IReadOnlyHandler.class); + if (handler instanceof IReadOnlyHandler2) { + return ((IReadOnlyHandler2) handler).canMakeWritable(axes, eObject).or(false); + } + } + return false; + } + + /** + * Tests if a resource that is read only could possibly be made writable by some means (file system attributes, team provider hook, database + * permissions, etc.) + * + * @param resource + * a resource that is assumed to be read-only + * @param domain + * the editing domain context of the {@link resource} + * @return + * whether the {@code resource} could be made writable + */ + public static boolean canMakeWritable(final Resource resource, final EditingDomain domain) { + return canMakeWritable(ReadOnlyAxis.anyAxis(), resource, domain); + } + + /** + * Tests if a resource that is read only could possibly be made writable according to any of + * the specified {@code axes} of read-only-ness. + * + * @param axes + * a set if orthogonal axes of read-only-ness to consider. May be empty, but that would not be especially useful + * @param resource + * a resource that is assumed to be read-only + * @param domain + * the editing domain context of the {@link resource} + * @return + * whether the {@code resource} could be made writable + */ + public static boolean canMakeWritable(Set<ReadOnlyAxis> axes, final Resource resource, final EditingDomain domain) { + if (domain != null) { + Object handler = PlatformHelper.getAdapter(domain, IReadOnlyHandler.class); + if (handler instanceof IReadOnlyHandler2) { + return ((IReadOnlyHandler2) handler).canMakeWritable(axes, new URI[] { resource.getURI() }).or(false); + } + } + return false; + } + + /** + * Tests if the given EStructuralFeature is required (ie. should always + * have a value) + * + * A feature is required if at least of one the following conditions if + * true : + * + * - It has a defaultValue + * - Its lowerBound is at least 1 + * - It is an enumeration (Enumerations always have a default value) + * - It is a Java primitive type, and is not marked as Unsettable + * + * @param feature + * the feature to test + * @return + * true if the feature is required, false otherwise + */ + public static boolean isRequired(final EStructuralFeature feature) { + EClassifier eType = feature.getEType(); + if (eType == null) { + return false; + } + + // EEnums are always required, as an EEnum always has a default value + if (eType instanceof EEnum) { + return true; + } + + // At least one value means it is required + if (feature.getLowerBound() >= 1) { + return true; + } + + // Java primitive types cannot have a null value + // if the feature is not specifically marked as unsettable, then it is required + if (eType.getInstanceClass() != null && eType.getInstanceClass().isPrimitive() && !feature.isUnsettable()) { + return true; + } + + // If there is a default value, there is always a value + if (feature.getDefaultValueLiteral() != null) { + return true; + } + + return false; // The property if not required + } + + /** + * Returns all objects of type T contained in the resource + * + * @param resource + * @param type + * @return + */ + public static <T> Set<T> allInstances(final Resource resource, Class<T> type) { + TreeIterator<EObject> iterator = resource.getAllContents(); + Set<T> result = new LinkedHashSet<T>(); + + while (iterator.hasNext()) { + EObject element = iterator.next(); + if (type.isInstance(element)) { + result.add(type.cast(element)); + } + } + + return result; + } + + /** + * Returns all the EPackages and nested EPackages contained in this resource + * + * @param resource + * @return + */ + public static Set<EPackage> getAllEPackages(final Resource resource) { + Set<EPackage> result = new LinkedHashSet<EPackage>(); + + for (EObject rootElement : resource.getContents()) { + if (rootElement instanceof EPackage) { + result.add((EPackage) rootElement); + result.addAll(getAllNestedPackages((EPackage) rootElement)); + } + } + + return result; + } + + /** + * Returns all packages nested in the given EPackage (recursively). Does not + * include the base EPackage. + * + * @param basePackage + * @return + */ + public static Set<EPackage> getAllNestedPackages(EPackage basePackage) { + Set<EPackage> result = new LinkedHashSet<EPackage>(); + + for (EPackage nestedPackage : basePackage.getESubpackages()) { + result.add(nestedPackage); + result.addAll(getAllNestedPackages(nestedPackage)); + } + + return result; + } + + /** + * + * @param resource + * a resource + * + * @return + * the list of the metamodels known by the resource + */ + public static Set<EPackage> getMetamodels(final Resource resource) { + Set<EPackage> metamodels = new HashSet<EPackage>(); + if (resource != null) { + final List<EObject> contents = new ArrayList<EObject>(resource.getContents()); + for (final EObject current : contents) { + metamodels.add(current.eClass().getEPackage()); + } + } + return metamodels; + } + + /** + * + * Returns the XMI ID of the given {@link EObject} or <code>null</code> if it cannot be resolved. + * + * @param object + * Object which we seek the XMI ID of. + * @return <code>object</code>'s XMI ID, <code>null</code> if not applicable. + */ + public static final String getXMIID(final EObject object) { + String objectID = null; + if (object != null && object.eResource() instanceof XMIResource) { + objectID = ((XMIResource) object.eResource()).getID(object); + } + return objectID; + } + + + + /** + * Gets the usages. + * + * @param source + * the source + * + * @return the usages or null if there is no usages + */ + public static Collection<Setting> getUsages(EObject source) { + // the functional code is defined in core because we need it in infra.core + // but infra.core can't depend on infra.emf (circular dependency) + return org.eclipse.papyrus.infra.core.utils.EMFHelper.getUsages(source); + } + + + /** + * <pre> + * Test if the used element is referenced by other elements than the known + * referencer (except its container). It ignores references from an other meta-model. + * </pre> + * + * @param usedObject + * the used object + * @param knownReferencer + * the known referencer + * @return true if the known referencer is the only referencer. + */ + public static boolean isOnlyUsage(EObject usedObject, EObject knownReferencer) { + boolean isUsed = false; + EPackage mmPackage = usedObject.eClass().getEPackage(); + + // Retrieve the list of elements referencing the usedObject. + Set<EObject> crossReferences = new HashSet<EObject>(); + for (Setting setting : getUsages(usedObject)) { + EObject eObj = setting.getEObject(); + if (eObj.eClass().getEPackage().equals(mmPackage)) { + crossReferences.add(eObj); + } + } + + // Remove the container of used object. + crossReferences.remove(usedObject.eContainer()); + // Remove the knownReferencer from the list of references. + crossReferences.remove(knownReferencer); + + // If no referencer remains in the list, the known element is the only + // usage. + if (crossReferences.isEmpty()) { + isUsed = true; + } + + return isUsed; + } + + /** + * + * @param superType + * an eclassifier + * @param subType + * another eClassifier + * @return + * <code>true</code> if the 2nd {@link EClassifier} is a subtype of the first one + */ + public static boolean isSuperType(final EClassifier superType, final EClassifier subType) { + if (superType == subType) { + return true; + } + + if (superType instanceof EClass && subType instanceof EClass) { + // special case because isSuperTypeOf doesn't handle it + if (superType == EcorePackage.eINSTANCE.getEObject()) { + return true; + } + + EClass superTypeEClass = (EClass) superType; + EClass subTypeEClass = (EClass) subType; + return superTypeEClass.isSuperTypeOf(subTypeEClass); + } + + // manage EDtataType + if (superType == EcorePackage.eINSTANCE.getEDataType() && subType instanceof EDataType) { + return true; + } + + return false; + } + + /** + * Computes the path from the root EObject to the given element, as a List of EObjects + * + * @param element + * @return + */ + public static List<EObject> getContainmentPath(EObject element) { + List<EObject> result; + if (element.eContainer() == null) { + result = new LinkedList<EObject>(); + result.add(element); + return result; + } else { + result = getContainmentPath(element.eContainer()); + result.add(element); + } + return result; + } + + /** + * Returns the given element, reloaded into the resource set of the context element, + * or the source element itself if not possible. + * + * Use this method for e.g. loading an element from a shared resource set into another resource set + * (Apply a registered profile/library, drop an element from the project explorer, ...) + * + * @param element + * @param contextElement + * @return + */ + public static <T extends EObject> T reloadIntoContext(T element, EObject contextElement) { + ResourceSet sourceResourceSet = getResourceSet(element); + ResourceSet loadingContext = getResourceSet(contextElement); + + if (sourceResourceSet == loadingContext || loadingContext == null) { + return element; + } + + URI sourceURI = EcoreUtil.getURI(element); + EObject result = loadingContext.getEObject(sourceURI, true); + + return (T) result; + } + + /** + * Returns the resourceSet owning this eObject, or null if it is detached + * + * @param eObject + */ + public static ResourceSet getResourceSet(EObject eObject) { + Resource resource = (eObject == null) ? null : eObject.eResource(); + return (resource == null) ? null : resource.getResourceSet(); + } + + /** + * Best-effort loads a resource, returning the first root element of the requested {@code type}. Unlike the {@link #loadChecked(ResourceSet, URI, Class) loadChecked} method, this will never throw an exception. + * + * @param rset + * a resource set in which to load the resource + * @param uri + * the URI of the resource to load + * @param type + * the type of root element to retrieve + * + * @return the requested root element, or {@code null} if the resource does not contain such an element or could not be loaded + * + * @see #loadChecked(ResourceSet, URI, Class) + */ + public static <T extends EObject> T load(ResourceSet rset, URI uri, Class<T> type) { + T result = null; + + try { + result = loadChecked(rset, uri, type); + } catch (Exception e) { + Activator.log.error("Exception in loading resource " + uri, e); //$NON-NLS-1$ + + // Maybe it was partially loaded? If so, try again + Resource res = rset.getResource(uri, false); + if ((res != null) && res.isLoaded()) { + result = Iterables.getFirst(Iterables.filter(res.getContents(), type), null); + } + } + + return result; + } + + /** + * Best-effort loads a resource, returning the first root element of the requested {@code type}. + * + * @param rset + * a resource set in which to load the resource + * @param uri + * the URI of the resource to load + * @param type + * the type of root element to retrieve + * + * @return the requested root element, or {@code null} if the resource does not contain such an element or could not be loaded + * @throws IOException + * on an I/O problem in loading the resource + * @throw RuntimeException on any other unforeseen (usually programming error) problem + * + * @see #load(ResourceSet, URI, Class) + */ + public static <T extends EObject> T loadChecked(ResourceSet rset, URI uri, Class<T> type) throws IOException { + try { + return Iterables.getFirst(Iterables.filter(rset.getResource(uri, true).getContents(), type), null); + } catch (WrappedException e) { + if (e.exception() instanceof IOException) { + throw (IOException) e.exception(); + } else if (e.exception() instanceof RuntimeException) { + throw (RuntimeException) e.exception(); + } else { + throw e; + } + } + } +} |