/******************************************************************************* * 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: ******************************************************************************/ package org.eclipse.emf.emfstore.common; import java.util.HashSet; 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 * */ public final class CommonUtil { /** * Private constructor since this is a utility class. */ private CommonUtil() { // nothing to do } private static Set<EClass> modelElementEClasses; /** * @param newMEInstance {@link EObject} the new modelElement instance. * @return EReference the Container * @param parent The EObject to get containment references from */ public static EReference getPossibleContainingReference(final EObject newMEInstance, EObject parent) { // the value of the 'EAll Containments' reference list. List<EReference> eallcontainments = parent.eClass().getEAllContainments(); EReference reference = null; for (EReference containmentitem : eallcontainments) { EClass eReferenceType = containmentitem.getEReferenceType(); if (eReferenceType.equals(newMEInstance)) { reference = containmentitem; break; } else if (eReferenceType.equals(EcorePackage.eINSTANCE.getEObject()) || eReferenceType.isSuperTypeOf(newMEInstance.eClass())) { reference = containmentitem; break; } } return reference; } /** * Gives all eClasses which can be contained in a given eClass. * * @param eClass the EClass * @return all Classes which can be contained */ public static Set<EClass> getAllEContainments(EClass eClass) { List<EReference> containments = eClass.getEAllContainments(); Set<EClass> eClazz = new HashSet<EClass>(); for (EReference ref : containments) { EClass eReferenceType = ref.getEReferenceType(); eClazz.addAll(getAllSubEClasses(eReferenceType)); } return eClazz; } /** * 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(EClass eClass) { Set<EClass> allEClasses = getAllModelElementEClasses(); if (EcorePackage.eINSTANCE.getEObject().equals(eClass)) { return allEClasses; } Set<EClass> result = new HashSet<EClass>(); for (EClass subClass : allEClasses) { 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 that are model element subclasses. * * @return a set of EClasses */ public static Set<EClass> getAllModelElementEClasses() { if (modelElementEClasses != null) { return new HashSet<EClass>(modelElementEClasses); } Set<EClass> result = new HashSet<EClass>(); Registry registry = EPackage.Registry.INSTANCE; for (Entry<String, Object> entry : new HashSet<Entry<String, Object>>(registry.entrySet())) { try { EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(entry.getKey()); result.addAll(getAllModelElementEClasses(ePackage)); } // BEGIN SUPRESS CATCH EXCEPTION catch (RuntimeException exception) { Activator.getDefault().logException("Failed to load model package " + entry.getKey(), exception); // END SUPRESS CATCH EXCEPTION } } modelElementEClasses = 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 */ private static Set<EClass> getAllModelElementEClasses(EPackage ePackage) { Set<EClass> result = new HashSet<EClass>(); for (EPackage subPackage : ePackage.getESubpackages()) { result.addAll(getAllModelElementEClasses(subPackage)); } for (EClassifier classifier : ePackage.getEClassifiers()) { if (classifier instanceof EClass) { 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(Class<T> parent, EObject child) { Set<EObject> seenModelElements = new HashSet<EObject>(); seenModelElements.add(child); return getParent(parent, child, seenModelElements); } @SuppressWarnings("unchecked") private static <T extends EObject> T getParent(Class<T> parent, EObject child, Set<EObject> seenModelElements) { if (child == null) { return null; } if (seenModelElements.contains(child.eContainer())) { throw new IllegalStateException("ModelElement is in a containment cycle"); } if (parent.isInstance(child)) { return (T) child; } else { seenModelElements.add(child); return getParent(parent, child.eContainer(), seenModelElements); } } /** * Check an Eobject and its containment tree whether it is selfcontained. A containment tree is self contained if it * does not have references to eobjects outside the tree. * * @param object the eObject * @return true if it is selfcontained */ public static boolean isSelfContained(EObject object) { return isSelfContained(object, false); } /** * 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 * @param ignoreContainer true if references of object to its container should be ignored in the check. * @return true if it is self0contained */ public static boolean isSelfContained(EObject object, boolean ignoreContainer) { Set<EObject> allChildEObjects = getNonTransientContents(object); Set<EObject> allEObjects = new HashSet<EObject>(allChildEObjects); allEObjects.add(object); Set<EObject> nonTransientCrossReferences = getNonTransientCrossReferences(object); if (ignoreContainer && object.eContainer() != null) { nonTransientCrossReferences.remove(object.eContainer()); } if (!allEObjects.containsAll(nonTransientCrossReferences)) { 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; } /** * Check if containment tree is in given resource * @param root the root of the tree * @param resource the resource to check for * @return true iff all contents of the containment tree of root are in the given resource */ public static boolean isContainedInResource(EObject root, Resource resource) { Set<EObject> allChildEObjects = getNonTransientContents(root); Set<EObject> allEObjects = new HashSet<EObject>(allChildEObjects); allEObjects.add(root); for (EObject eObject: allEObjects) { if (resource!=eObject.eResource()) { return false; } } return true; } @SuppressWarnings("unchecked") private static Set<EObject> getNonTransientContents(EObject object) { Set<EObject> result = new HashSet<EObject>(); if (object == null) { return result; } for (EReference containmentReference : object.eClass().getEAllContainments()) { if (!containmentReference.isTransient()) { Object contentObject = object.eGet(containmentReference, true); if (containmentReference.isMany()) { EList<? extends EObject> contentList = (EList<? extends EObject>) contentObject; for (EObject content : contentList) { result.add(content); result.addAll(getNonTransientContents(content)); } } else { EObject content = (EObject) contentObject; if (content == null) { continue; } result.add(content); result.addAll(getNonTransientContents(content)); } } } return result; } @SuppressWarnings("unchecked") private static Set<EObject> getNonTransientCrossReferences(EObject object) { Set<EObject> result = new HashSet<EObject>(); if (object == null) { return result; } for (EReference reference : object.eClass().getEAllReferences()) { if (!reference.isTransient() && !reference.isContainment()) { Object referenceObject = object.eGet(reference, true); if (reference.isMany()) { EList<? extends EObject> referencesList = (EList<? extends EObject>) referenceObject; result.addAll(referencesList); for (EObject ref : referencesList) { if (CommonUtil.isSingletonEObject(ref)) { continue; } result.add(ref); } } else { 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 in question * * @return true if it is a singleton */ public static boolean isSingletonEObject(EObject eObject) { if (eObject.eContainer() != null && eObject.eContainer().equals(EcorePackage.eINSTANCE)) { return true; } return false; } // Validates if the EObjects can be imported private static Set<EObject> validation(Resource resource, List<String> errorStrings) { Set<EObject> childrenSet = new HashSet<EObject>(); Set<EObject> rootNodes = new HashSet<EObject>(); TreeIterator<EObject> contents = resource.getAllContents(); // 1. Run: Put all children in set while (contents.hasNext()) { 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()) { EObject content = contents.next(); if (!childrenSet.contains(content)) { rootNodes.add(content); } } // 3. Check if RootNodes are SelfContained -- yes: import -- no: error Set<EObject> notSelfContained = new HashSet<EObject>(); for (EObject rootNode : rootNodes) { if (!CommonUtil.isSelfContained(rootNode)) { errorStrings.add(rootNode + " is not self contained\n"); 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(Resource resource, List<String> errorStrings) { Set<EObject> result = new HashSet<EObject>(); result = validation(resource, errorStrings); return result; } }