/***************************************************************************** * Copyright (c) 2010 CEA LIST. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation *****************************************************************************/ 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.emf.common.util.TreeIterator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; 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.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.xmi.XMIResource; import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; import org.eclipse.emf.edit.domain.EditingDomain; import org.eclipse.emf.facet.custom.ui.CustomizedContentProviderUtils; import org.eclipse.papyrus.infra.core.services.ServiceException; import org.eclipse.papyrus.infra.core.utils.ServiceUtilsForActionHandlers; import org.eclipse.papyrus.infra.emf.Activator; /** * 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) { Activator.log.warn("Class " + className + " not found in Metamodel : " + metamodel.getName() + " (" + metamodel.getNsURI() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ return false; } return theClass.isInstance(element); } /** * 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 EObjects, and do not implement IAdatapble. Object resolved = CustomizedContentProviderUtils.resolve(source); if(resolved != source && resolved instanceof EObject) { return (EObject)resolved; } //General case if(source instanceof EObject) { return (EObject)source; } else if(source instanceof IAdaptable) { EObject eObject = (EObject)((IAdaptable)source).getAdapter(EObject.class); if(eObject == null) { //EMF Facet 0.1 eObject = (EObject)((IAdaptable)source).getAdapter(EReference.class); } return eObject; } return 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 = ServiceUtilsForActionHandlers.getInstance().getTransactionalEditingDomain(); } 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 { if(resourceSet == null) { 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; } /** * 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()); } /** * 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); //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 * Delegates to the EObject's editing domain if it can be found * * @param eObject * @return * True if the EObject is read only */ public static boolean isReadOnly(final EObject eObject) { EditingDomain domain = resolveEditingDomain(eObject); return isReadOnly(eObject, domain); } /** * Tests if an EObject is read only * Delegates to the given editing domain if it isn't null * * @param eObject * @param domain * @return * True if the EObject is read only */ public static boolean isReadOnly(final EObject eObject, final EditingDomain domain) { return isReadOnly(eObject.eResource(), domain); } /** * Tests if the Resource is read only * Delegates to the given editing domain if it isn't null * * @param resource * @param domain * @return * True if the Resource is read only */ public static boolean isReadOnly(final Resource resource, final EditingDomain domain) { if(domain instanceof AdapterFactoryEditingDomain) { return ((AdapterFactoryEditingDomain)domain).isReadOnly(resource); } if(resource == null) { return false; } 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 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) { //EEnums are always required, as an EEnum always has a default value if(feature.getEType() 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(feature.getEType().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; } }