/******************************************************************************* * Copyright (c) 2008-2011 Chair for Applied Software Engineering, * Technische Universitaet Muenchen. * 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: * koegel ******************************************************************************/ package org.eclipse.emf.emfstore.internal.common; import java.util.LinkedHashSet; import java.util.List; import java.util.Map.Entry; import java.util.Set; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.TreeIterator; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EPackage.Registry; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EcorePackage; import org.eclipse.emf.ecore.resource.Resource; /** * Utility class to retrieve meta model information. * * @author koegel * */ // TODO: check whether loadFromResource and validation may be removed public final class CommonUtil { private static boolean testing; private static Set<EClass> registryEClasses; /** * Private constructor since this is a utility class. */ private CommonUtil() { // nothing to do } /** * Returns a containing reference of the <code>parent</code> that may contain the * given <code>containee</code>. * * @param containee * the EObject that may be contained by the parent's reference, if any * @param parent The EObject to get the containment references from * @return the <code>parent</code>'s container reference */ public static EReference getPossibleContainingReference(final EObject containee, final EObject parent) { final List<EReference> eAllContainments = parent.eClass().getEAllContainments(); EReference reference = null; for (final EReference containmentItem : eAllContainments) { final EClass eReferenceType = containmentItem.getEReferenceType(); if (eReferenceType.equals(containee)) { reference = containmentItem; break; } else if (eReferenceType.equals(EcorePackage.eINSTANCE.getEObject()) || eReferenceType.isSuperTypeOf(containee.eClass())) { reference = containmentItem; break; } } return reference; } /** * Gives all registryEClasses which can be contained in a given EClass. * * @param eClass * the EClass * @return all Classes which can be contained */ public static Set<EClass> getAllEContainments(final EClass eClass) { final List<EReference> containments = eClass.getEAllContainments(); final Set<EClass> eClasses = new LinkedHashSet<EClass>(); for (final EReference ref : containments) { final EClass eReferenceType = ref.getEReferenceType(); eClasses.addAll(getAllSubEClasses(eReferenceType)); } return eClasses; } /** * Retrieve all EClasses from the ECore package registry that are subclasses of the given EClass. Does not include * abstract classes or interfaces. * * @param eClass the superClass of the subClasses to retrieve * @return a set of EClasses */ public static Set<EClass> getAllSubEClasses(final EClass eClass) { final Set<EClass> allEClasses = getAllModelElementEClasses(); if (EcorePackage.eINSTANCE.getEObject().equals(eClass)) { return allEClasses; } final Set<EClass> result = new LinkedHashSet<EClass>(); for (final EClass subClass : allEClasses) { final boolean isSuperTypeOf = eClass.isSuperTypeOf(subClass) || eClass.equals(EcorePackage.eINSTANCE.getEObject()); if (isSuperTypeOf && !subClass.isAbstract() && !subClass.isInterface()) { result.add(subClass); } } return result; } /** * Retrieve all EClasses from the ECore package registry. * * @return a set of EClasses */ public static Set<EClass> getAllModelElementEClasses() { if (registryEClasses != null) { return new LinkedHashSet<EClass>(registryEClasses); } final Set<EClass> result = new LinkedHashSet<EClass>(); final Registry registry = EPackage.Registry.INSTANCE; for (final Entry<String, Object> entry : new LinkedHashSet<Entry<String, Object>>(registry.entrySet())) { try { final EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(entry.getKey()); result.addAll(getAllModelElementEClasses(ePackage)); } // BEGIN SUPRESS CATCH EXCEPTION catch (final RuntimeException exception) { Activator.getDefault().logException(Messages.CommonUtil_LoadingPackageFailed + entry.getKey(), exception); // END SUPRESS CATCH EXCEPTION } } registryEClasses = result; return result; } /** * Retrieve all EClasses from the ECore package that are model element subclasses. * * @param ePackage * the package to get the classes from * @return a set of EClasses found in the given EPackage */ private static Set<EClass> getAllModelElementEClasses(final EPackage ePackage) { final Set<EClass> result = new LinkedHashSet<EClass>(); for (final EPackage subPackage : ePackage.getESubpackages()) { result.addAll(getAllModelElementEClasses(subPackage)); } for (final EClassifier classifier : ePackage.getEClassifiers()) { if (classifier instanceof EClass) { final EClass subEClass = (EClass) classifier; result.add(subEClass); } } return result; } /** * Get the EContainer that contains the given model element and whose EContainer is null. * * @param <T> parent type * @param parent the Class of the parent * @param child the model element whose container should get returned * @return the container */ public static <T extends EObject> T getParent(final Class<T> parent, final EObject child) { final Set<EObject> seenModelElements = new LinkedHashSet<EObject>(); seenModelElements.add(child); return getParent(parent, child, seenModelElements); } @SuppressWarnings("unchecked") private static <T extends EObject> T getParent(final Class<T> parent, final EObject child, final Set<EObject> seenModelElements) { if (child == null) { return null; } if (seenModelElements.contains(child.eContainer())) { throw new IllegalStateException(Messages.CommonUtil_ContainmentCycle); } if (parent.isInstance(child)) { return (T) child; } seenModelElements.add(child); return getParent(parent, child.eContainer(), seenModelElements); } /** * Check an EObject and its containment tree whether it is self-contained. A containment tree is self contained if * it * does not have references to EObjects outside the tree. * * @param object * the eObject that is checked whether it is self-contained * @return true if it is self-contained, false otherwise */ public static boolean isSelfContained(final EObject object) { return isSelfContained(object, false); } /** * Check an EObject and its containment tree whether it is self-contained.<br/> * A containment tree is self contained if it does not have references to EObjects outside the tree. * * @param eObject * the EObject that is checked whether it is self-contained * @param ignoreContainer * true, if references of object to its container should be ignored in the check * @return true if it is self-contained, false otherwise */ public static boolean isSelfContained(final EObject eObject, final boolean ignoreContainer) { final Set<EObject> allChildEObjects = getNonTransientContents(eObject); final Set<EObject> allEObjects = new LinkedHashSet<EObject>(allChildEObjects); allEObjects.add(eObject); final Set<EObject> nonTransientReferences = getNonTransientCrossReferences(eObject); if (ignoreContainer && eObject.eContainer() != null) { nonTransientReferences.remove(eObject.eContainer()); } if (!allEObjects.containsAll(nonTransientReferences)) { return false; } // TOOD: EM, handle ignored data types here // check if only cross references to known elements exist // for (EObject content : allChildEObjects) { // if (!allEObjects.containsAll(getNonTransientCrossReferences(content))) { // return false; // } // } return true; } /** * Get all contained EObjects not including transient containment features. * * @param eObject * the EObject whose non-transient contents should be retrieved * @return a set of contained elements not including root (the passed EObject itself) */ public static Set<EObject> getNonTransientContents(final EObject eObject) { final Set<EObject> result = new LinkedHashSet<EObject>(); if (eObject == null) { return result; } for (final EReference containmentReference : eObject.eClass().getEAllContainments()) { if (!containmentReference.isTransient()) { final Object contentObject = eObject.eGet(containmentReference, true); if (containmentReference.isMany()) { @SuppressWarnings("unchecked") final EList<? extends EObject> contentList = (EList<? extends EObject>) contentObject; for (final EObject content : contentList) { result.add(content); result.addAll(getNonTransientContents(content)); } } else { final EObject content = (EObject) contentObject; if (content == null) { continue; } result.add(content); result.addAll(getNonTransientContents(content)); } } } return result; } @SuppressWarnings("unchecked") private static Set<EObject> getNonTransientCrossReferences(final EObject object) { final Set<EObject> result = new LinkedHashSet<EObject>(); if (object == null) { return result; } for (final EReference reference : object.eClass().getEAllReferences()) { if (!reference.isTransient() && !reference.isContainment()) { final Object referenceObject = object.eGet(reference, true); if (reference.isMany()) { final EList<? extends EObject> referencesList = (EList<? extends EObject>) referenceObject; result.addAll(referencesList); for (final EObject ref : referencesList) { if (CommonUtil.isSingletonEObject(ref)) { continue; } result.add(ref); } } else { final EObject crossReference = (EObject) referenceObject; if (crossReference == null || CommonUtil.isSingletonEObject(crossReference)) { continue; } result.add(crossReference); } } } return result; } /** * Determines whether an EObject is a singleton object. All EObjects being children of ECorePackage are considered * as singletons. * * @param eObject * the EObject that will be checked whether it is a singleton * @return true if it is a singleton, false otherwise */ public static boolean isSingletonEObject(final EObject eObject) { if (eObject.eContainer() != null && eObject.eContainer().equals(EcorePackage.eINSTANCE)) { return true; } return false; } /** * Loads a Set of EObject from a given resource. Content which couldn't be loaded creates a error string which will * be added to the errorStrings list. After the return from the method to the caller the return value contains the * loaded EObjects. * * @param resource contains the items which should be loaded. * @param errorStrings contains all messages about items which couldn't be loaded by the method. * @return Set with the loaded an valid EObjects */ private static Set<EObject> validation(final Resource resource, final List<String> errorStrings) { final Set<EObject> childrenSet = new LinkedHashSet<EObject>(); final Set<EObject> rootNodes = new LinkedHashSet<EObject>(); TreeIterator<EObject> contents = resource.getAllContents(); // 1. Run: Put all children in set while (contents.hasNext()) { final EObject content = contents.next(); childrenSet.addAll(content.eContents()); } contents = resource.getAllContents(); // 2. Run: Check if RootNodes are children -> set.contains(RootNode) // -- no: RootNode in rootNode-Set // -- yes: Drop RootNode, will be imported as a child while (contents.hasNext()) { final EObject content = contents.next(); if (!childrenSet.contains(content)) { rootNodes.add(content); } } // 3. Check if RootNodes are SelfContained -- yes: import -- no: error final Set<EObject> notSelfContained = new LinkedHashSet<EObject>(); for (final EObject rootNode : rootNodes) { if (!CommonUtil.isSelfContained(rootNode)) { errorStrings.add(rootNode + Messages.CommonUtil_NotSelfContained); notSelfContained.add(rootNode); } } rootNodes.removeAll(notSelfContained); return rootNodes; } /** * Loads a Set of EObject from a given resource. Content which couldn't be loaded creates a error string which will * be added to the errorStrings list. After the return from the method to the caller the return value contains the * loaded EObjects. * * @param resource contains the items which should be loaded. * @param errorStrings contains all messages about items which couldn't be loaded by the method. * @return Set with the loaded an valid EObjects */ public static Set<EObject> loadFromResource(final Resource resource, final List<String> errorStrings) { return validation(resource, errorStrings); } /** * If we are running tests. In this case the workspace will be created in * USERHOME/.emfstore.test. * * @param testing * the testing to set */ public static void setTesting(final boolean testing) { CommonUtil.testing = testing; } /** * @return if we are running tests. In this case the workspace will be * created in USERHOME/.emfstore.test. */ public static boolean isTesting() { return testing; } /** * Returns the file encoding in use. * * @return the file encoding */ public static String getEncoding() { return "UTF-8"; //$NON-NLS-1$ } }