/*******************************************************************************
* 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.model.util;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.EList;
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.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.EcoreFactory;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EContentsEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.emfstore.common.CommonUtil;
import org.eclipse.emf.emfstore.common.model.AssociationClassElement;
import org.eclipse.emf.emfstore.common.model.IdEObjectCollection;
import org.eclipse.emf.emfstore.common.model.ModelElementId;
import org.eclipse.emf.emfstore.common.model.ModelFactory;
import org.eclipse.emf.emfstore.common.model.Project;
import org.eclipse.emf.emfstore.common.model.SingletonIdResolver;
import org.eclipse.emf.emfstore.common.model.impl.ProjectImpl;
import org.xml.sax.InputSource;
/**
* Utility class for ModelElements.
*
* @author koegel
*/
public final class ModelUtil {
private static final String ORG_ECLIPSE_EMF_EMFSTORE_COMMON_MODEL = "org.eclipse.emf.emfstore.common.model";
/**
* Text marker for the begin of the plaintext in rich text attributes.
*/
public static final String BEGINTEXT_TOKEN = "%BEGINNTEXT%";
/**
* URI used to serialize EObject with the model util.
*/
public static final URI VIRTUAL_URI = URI.createURI("virtualUri");
/**
* Contains the canonical names of classes which will be ignored.
*/
private static Set<String> ignoredDataTypes;
/**
* Contains all ID resolvers for singleton datatypes.
*/
private static Set<SingletonIdResolver> singletonIdResolvers;
private static HashMap<Object, Object> resourceLoadOptions;
private static HashMap<Object, Object> resourceSaveOptions;
/**
* Private constructor.
*/
private ModelUtil() {
// nothing to do
}
/**
* Creates a ModelElementId object from a string.
*
* @param id
* as string
* @return id as object
*/
public static ModelElementId createModelElementId(String id) {
ModelElementId modelElementId = ModelFactory.eINSTANCE.createModelElementId();
modelElementId.setId(id);
return modelElementId;
}
/**
* Compares two EObject by checking whether the string representations of
* the EObjects are equal.
*
* @param eobjectA
* the first EObject
* @param eobjectB
* the second EObject
* @return true if the two objects are equal
*/
public static boolean areEqual(EObject eobjectA, EObject eobjectB) {
String stringA;
String stringB;
try {
stringA = eObjectToString(eobjectA);
stringB = eObjectToString(eobjectB);
} catch (SerializationException e) {
return false;
}
return stringA.equals(stringB);
}
/**
* Converts an EObject to a String.
*
* @param object
* the eObject
* @return String representation of the EObject
* @throws SerializationException
* if a serialization problem occurs
*/
public static String eObjectToString(EObject object) throws SerializationException {
boolean containmentCheckEnabled = false;
boolean hrefCheckEnabled = false;
boolean proxyCheckEnabled = false;
IConfigurationElement[] elements = Platform.getExtensionRegistry().getConfigurationElementsFor(
"org.eclipse.emf.emfstore.common.model.serializationoptions");
if (elements != null && elements.length > 0) {
hrefCheckEnabled = Boolean.parseBoolean(elements[0].getAttribute("HrefCheck"));
proxyCheckEnabled = Boolean.parseBoolean(elements[0].getAttribute("ProxyCheck"));
containmentCheckEnabled = Boolean.parseBoolean(elements[0].getAttribute("SelfContainmentCheck"));
}
return eObjectToString(object, !containmentCheckEnabled, !hrefCheckEnabled, !proxyCheckEnabled);
}
/**
* Converts an {@link EObject} to a {@link String}.
*
* @param object
* the {@link EObject}
* @param overrideContainmentCheck
* if true, no containment check is performed
* @param overrideHrefCheck
* checks whether there is a <code>href</code> in the serialized
* text
* @param overrideProxyCheck
* if true, proxy check is ignored
* @return String representation of the {@link EObject}
* @throws SerializationException
* if a serialization problem occurs
*/
public static String eObjectToString(EObject object, boolean overrideContainmentCheck, boolean overrideHrefCheck,
boolean overrideProxyCheck) throws SerializationException {
if (object == null) {
return null;
}
XMIResource res = (XMIResource) (new ResourceSetImpl()).createResource(VIRTUAL_URI);
EObject copy;
if (object instanceof Project) {
Project project = (Project) object;
Project copiedProject = (Project) clone(object);
for (ModelElementId modelElementId : project.getAllModelElementIds()) {
if (isIgnoredDatatype(project.getModelElement(modelElementId))) {
continue;
}
res.setID(copiedProject.getModelElement(modelElementId), modelElementId.getId());
}
res.getContents().add(copiedProject);
copy = copiedProject;
} else {
copy = EcoreUtil.copy(object);
res.getContents().add(copy);
}
if (!overrideContainmentCheck && !(copy instanceof EClass)) {
if (!CommonUtil.isSelfContained(copy) || !CommonUtil.isContainedInResource(copy, res)) {
throw new SerializationException(copy);
}
}
int step = 200;
int initialSize = step;
if (object instanceof Project) {
Project project = (Project) object;
initialSize = project.getAllModelElements().size() * step;
}
if (!overrideProxyCheck) {
proxyCheck(res);
}
StringWriter stringWriter = new StringWriter(initialSize);
try {
res.save(stringWriter, getResourceSaveOptions());
} catch (IOException e) {
throw new SerializationException(e);
}
String result = stringWriter.toString();
if (!overrideHrefCheck) {
hrefCheck(result);
}
return result;
}
/**
* @param result
*/
private static void proxyCheck(Resource resource) throws SerializationException {
EcoreUtil.resolveAll(resource);
TreeIterator<EObject> contents = resource.getAllContents();
while (contents.hasNext()) {
EObject eObject = contents.next();
if (eObject.eIsProxy()) {
throw new SerializationException("Serialization failed due to unresolved proxy detection.");
}
}
}
private static void hrefCheck(String result) throws SerializationException {
char[] needle = "href".toCharArray();
int pointer = 0;
boolean insideQuotes = false;
for (char character : result.toCharArray()) {
if (character == '"') {
insideQuotes = !insideQuotes;
}
if (!insideQuotes && character == needle[pointer]) {
if (++pointer == needle.length) {
throw new SerializationException("Serialization failed due to href detection.");
}
} else {
pointer = 0;
}
}
}
/**
* Determines whether the type of an EObject is an ignored one.
*
* @param eObject
* the EObject which is to be checked
* @return true, if the EObject will be ignored, false otherwise
*/
public static boolean isIgnoredDatatype(EObject eObject) {
if (ignoredDataTypes == null) {
ignoredDataTypes = new HashSet<String>();
IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(
"org.eclipse.emf.emfstore.common.model.ignoredatatype");
for (IConfigurationElement extension : config) {
String className = extension.getAttribute("type");
ignoredDataTypes.add(className);
}
}
return ignoredDataTypes.contains(eObject.eClass().getInstanceClassName());
}
/**
* Converts a {@link String} to an {@link EObject}. <b>Note</b>: {@link String} must be the result of
* {@link ModelUtil#eObjectToString(EObject)}
*
* @param object
* the {@link String} representation of the {@link EObject}
* @return the deserialized {@link EObject}
* @throws SerializationException
* if deserialization fails
*/
public static EObject stringToEObject(String object) throws SerializationException {
if (object == null) {
return null;
}
XMIResource res = (XMIResource) (new ResourceSetImpl()).createResource(VIRTUAL_URI);
try {
res.load(new InputSource(new StringReader(object)), getResourceLoadOptions());
} catch (UnsupportedEncodingException e) {
throw new SerializationException(e);
} catch (IOException e) {
throw new SerializationException(e);
}
EObject result = res.getContents().get(0);
if (result instanceof Project) {
Project project = (Project) result;
Map<EObject, ModelElementId> eObjectToIdMap = new HashMap<EObject, ModelElementId>();
Map<ModelElementId, EObject> idToEObjectMap = new HashMap<ModelElementId, EObject>();
TreeIterator<EObject> it = ((Project) result).eAllContents();
while (it.hasNext()) {
EObject me = it.next();
String id;
if (ModelUtil.isIgnoredDatatype(me)) {
// create random ID for generic types, won't get serialized
// anyway
id = ModelFactory.eINSTANCE.createModelElementId().getId();
} else {
id = res.getID(me);
}
if (id == null) {
throw new SerializationException("Failed to retrieve ID for EObject contained in project: " + me);
}
ModelElementId meId = ModelFactory.eINSTANCE.createModelElementId();
meId.setId(id);
eObjectToIdMap.put(me, meId);
idToEObjectMap.put(meId, me);
}
project.initCaches(eObjectToIdMap, idToEObjectMap);
}
// TODO: added to resolve model element map in a CreateDeleteOp
// check whether we can generalize this
EcoreUtil.resolveAll(result);
res.getContents().remove(result);
return result;
}
/**
* Delivers a map of options for loading resources. Especially {@link XMLResource#OPTION_DEFER_IDREF_RESOLUTION}
* which speeds up loading
* due to our id based resources.
*
* @return map of options for {@link XMIResource} or {@link XMLResource}.
*/
public static Map<Object, Object> getResourceLoadOptions() {
if (resourceLoadOptions == null) {
resourceLoadOptions = new HashMap<Object, Object>();
// options.put(XMLResource.OPTION_CONFIGURATION_CACHE, true)
resourceLoadOptions.put(XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE, Boolean.TRUE);
resourceLoadOptions.put(XMLResource.OPTION_DEFER_IDREF_RESOLUTION, true);
}
return resourceLoadOptions;
}
/**
* Delivers a map of mandatory options for saving resources.
*
* @return map of options for {@link XMIResource} or {@link XMLResource}.
*/
public static Map<Object, Object> getResourceSaveOptions() {
if (resourceSaveOptions == null) {
resourceSaveOptions = new HashMap<Object, Object>();
resourceSaveOptions.put(XMLResource.OPTION_USE_ENCODED_ATTRIBUTE_STYLE, Boolean.TRUE);
}
return resourceSaveOptions;
}
private static boolean canHaveInstances(EClass eClass) {
return !(eClass.isAbstract() || eClass.isInterface());
}
/**
* Recursively goes through model and create a list of all non-Abstract
* classes.
*
* @param ePackage
* the package to start with.
* @return list of all non-Abstract model element classes in starting
* package and its sub-packages
*/
public static Set<EClass> getNonAbstractMETypes(EPackage ePackage) {
Set<EClass> nonAbstractMETypes = new HashSet<EClass>();
Set<EClass> allMETypes = getAllMETypes(ePackage);
Iterator<EClass> iterator = allMETypes.iterator();
while (iterator.hasNext()) {
EClass eClass = iterator.next();
if (canHaveInstances(eClass)) {
nonAbstractMETypes.add(eClass);
}
}
return nonAbstractMETypes;
}
/**
* Recursively goes through package and returns a list of all EClasses
* inheriting ModelElement (abstract classes and interfaces are also
* include).
*
* @param ePackage
* starting package
* @return a list of all EClasses inheriting ModelElement (inclusive
* abstract classes and interfaces) in starting package and all its
* sub-packages.
*/
public static Set<EClass> getAllMETypes(EPackage ePackage) {
Set<EClass> meTypes = new HashSet<EClass>();
for (EObject eObject : ePackage.eContents()) {
if (eObject instanceof EClass) {
EClass eClass = (EClass) eObject;
meTypes.add(eClass);
} else if (eObject instanceof EPackage) {
EPackage eSubPackage = (EPackage) eObject;
meTypes.addAll(getAllMETypes(eSubPackage));
}
}
return meTypes;
}
/**
* This will add a new entry to error log view of eclipse.
*
* @param message
* message
* @param exception
* exception
* @param statusInt
* severity. Use one of constants in
* org.eclipse.core.runtime.Status class.
* @throws LoggedException
*/
public static void log(String message, Throwable exception, int statusInt) {
Status status = new Status(statusInt, Platform.getBundle(ORG_ECLIPSE_EMF_EMFSTORE_COMMON_MODEL)
.getSymbolicName(), statusInt, message, exception);
Platform.getLog(Platform.getBundle(ORG_ECLIPSE_EMF_EMFSTORE_COMMON_MODEL)).log(status);
}
/**
* Log an exception to the platform log. This will create a popup in the ui.
*
* @param message
* the message
* @param exception
* the exception
*/
public static void logException(String message, Throwable exception) {
log(message, exception, IStatus.ERROR);
}
/**
* Log an exception to the platform log. This will create a popup in the ui.
*
* @param exception
* the exception
*/
public static void logException(Throwable exception) {
logException(exception.getMessage(), exception);
}
/**
* Log a warning to the platform log. This will NOT create a popup in the
* ui.
*
* @param message
* the message
* @param exception
* the exception
*/
public static void logWarning(String message, Throwable exception) {
log(message, exception, IStatus.WARNING);
}
/**
* Log a warning to the platform log. This will NOT create a popup in the
* ui.
*
* @param message
* the message
*/
public static void logWarning(String message) {
log(message, null, IStatus.WARNING);
}
/**
* Log an exception to the platform log. This will create a popup in the ui.
*
* @param message
* the message
*/
public static void logInfo(String message) {
log(message, null, IStatus.INFO);
}
/**
* Clone any EObject.
*
* @param <T>
* the Eobject sub type
* @param eObject
* the Eobject instance
* @return a clone of the Eobject instance
*/
@SuppressWarnings("unchecked")
public static <T extends EObject> T clone(T eObject) {
if (eObject instanceof ProjectImpl) {
return (T) ((ProjectImpl) eObject).copy();
}
EObject clone = EcoreUtil.copy(eObject);
return (T) clone;
}
/**
* Clone a list of EObjects.
*
* @param <T>
* the EObject sub type the list consists of
* @param list
* the list instance
* @return a clone of the list and its contents instance
*/
@SuppressWarnings("unchecked")
public static <T extends EObject> List<T> clone(List<T> list) {
ArrayList<T> result = new ArrayList<T>();
for (EObject eObject : list) {
T clone = (T) EcoreUtil.copy(eObject);
result.add(clone);
}
return result;
}
/**
* Create a flat clone of the list, the list if cloned but ot its content.
*
* @param <T>
* the list type parameter
* @param originalList
* the original list
* @return a flat copy
*/
public static <T extends EObject> List<T> flatCloneList(List<T> originalList) {
List<T> clonedList = new ArrayList<T>(originalList.size());
for (T element : originalList) {
clonedList.add(element);
}
return clonedList;
}
/**
* Load an EObject from a resource, the resource is supposed to contain only
* one root object of the given EClass type. Type T must match EClass type.
*
* @param <T>
* Type of the EObject
* @param eClass
* the EClass of the EObject
* @param resourceURI
* the resources URI
* @param checkConstraints
* whether to perform additional sanity checks. These checks
* basically try to enforce that a resource contains exactly one
* object.
* @return the object loaded from the resource
* @throws IOException
* if loading the object from the resource fails.
*/
@SuppressWarnings("unchecked")
public static <T extends EObject> T loadEObjectFromResource(EClass eClass, URI resourceURI, boolean checkConstraints)
throws IOException {
ResourceSet resourceSet = new ResourceSetImpl();
Resource resource;
if (checkConstraints) {
resource = resourceSet.getResource(resourceURI, false);
} else {
resource = resourceSet.getResource(resourceURI, true);
}
resource.load(getResourceLoadOptions());
EList<EObject> contents = resource.getContents();
if (checkConstraints) {
if (contents.size() > 1) {
throw new IOException("Resource containes multiple objects!");
}
}
if (contents.size() < 1) {
throw new IOException("Resource contains no objects");
}
EObject eObject = contents.get(0);
if (eObject instanceof Project && resource instanceof XMIResource) {
XMIResource xmiResource = (XMIResource) resource;
Project project = (Project) eObject;
Map<EObject, ModelElementId> eObjectToIdMap = new HashMap<EObject, ModelElementId>();
Map<ModelElementId, EObject> idToEObjectMap = new HashMap<ModelElementId, EObject>();
TreeIterator<EObject> it = project.eAllContents();
while (it.hasNext()) {
EObject obj = it.next();
ModelElementId objId = ModelFactory.eINSTANCE.createModelElementId();
String id = xmiResource.getID(obj);
if (id != null) {
objId.setId(id);
eObjectToIdMap.put(obj, objId);
idToEObjectMap.put(objId, obj);
}
}
project.initCaches(eObjectToIdMap, idToEObjectMap);
}
if (!(eClass.isInstance(eObject))) {
throw new IOException("Resource contains no objects of given class");
}
return (T) eObject;
}
/**
* Save a list of EObjects to the resource with the given URI.
*
* @param eObjects
* the EObjects to be saved
* @param resourceURI
* the URI of the resource, which should be used to save the
* EObjects
* @throws IOException
* if saving to the resource fails
*/
public static void saveEObjectToResource(List<? extends EObject> eObjects, URI resourceURI) throws IOException {
ResourceSet resourceSet = new ResourceSetImpl();
Resource resource = resourceSet.createResource(resourceURI);
EList<EObject> contents = resource.getContents();
for (EObject eObject : eObjects) {
contents.add(eObject);
if (eObject instanceof Project && resource instanceof XMIResource) {
setXmiIdsOnResource((Project) eObject, (XMIResource) resource);
}
}
contents.addAll(eObjects);
resource.save(null);
}
/**
* Set all IDs contained in the project as XMI IDs for the model elements in
* the project.
*
* @param project
* a project
* @param xmiResource
* the resource that will contain the XMI IDs
*/
public static void setXmiIdsOnResource(Project project, XMIResource xmiResource) {
for (EObject modelElement : project.getAllModelElements()) {
ModelElementId modelElementId = project.getModelElementId(modelElement);
xmiResource.setID(modelElement, modelElementId.getId());
}
}
/**
* Save an EObject to a resource.
*
* @param eObject
* the object
* @param resourceURI
* the resources URI
* @throws IOException
* if saving to the resource fails.
*/
public static void saveEObjectToResource(EObject eObject, URI resourceURI) throws IOException {
ArrayList<EObject> list = new ArrayList<EObject>();
list.add(eObject);
saveEObjectToResource(list, resourceURI);
}
/**
* Retrieve the current model version number.
*
* @return an integer identifing the current model version
* @throws MalformedModelVersionException
* if there is no well formed or defined model version
*/
public static int getModelVersionNumber() throws MalformedModelVersionException {
IConfigurationElement[] rawExtensions = Platform.getExtensionRegistry().getConfigurationElementsFor(
"org.eclipse.emf.emfstore.common.model.modelversion");
if (rawExtensions.length != 1) {
String message = "There is " + rawExtensions.length
+ " Model Version(s) registered for the given model. Migrator will assume model version 0.";
logInfo(message);
return 0;
}
IConfigurationElement extension = rawExtensions[0];
String string = extension.getAttribute("versionIdentifier");
try {
int version = Integer.parseInt(string);
return version;
} catch (NumberFormatException e) {
throw new MalformedModelVersionException("Version identifier was malformed, it must be an integer: "
+ string);
}
}
/**
* Get Project that contains a model element.
*
* @param modelElement
* the model element
* @return the project or null if the element is not contained in a project.
*/
public static Project getProject(EObject modelElement) {
Set<EObject> seenModelElements = new HashSet<EObject>();
seenModelElements.add(modelElement);
return (Project) getParent(Project.class, modelElement, seenModelElements);
}
/**
* Get the EContainer that contains the given model element and whose
* EContainer is null.
*
* @param parent
* the Class of the parent
* @param child
* the model element whose container should get returned
* @return the container
*/
public static EObject getParent(Class<? extends EObject> parent, EObject child) {
Set<EObject> seenModelElements = new HashSet<EObject>();
seenModelElements.add(child);
return getParent(parent, child, seenModelElements);
}
private static EObject getParent(Class<? extends EObject> 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 child;
} else {
seenModelElements.add(child);
return getParent(parent, child.eContainer(), seenModelElements);
}
}
/**
* Whether a {@link EClass} is a association class. Association classes are
* not displayed as dedicated elements. A link from one element to another
* which goes over an association class is displayed by a dedicated widget.
* This widgets allows to trace transparently without seeing the association
* class.
*
* @param eClazz
* the {@link EClass}
* @return if it is an association
*/
public static boolean isAssociationClassElement(EClass eClazz) {
if (eClazz == null || eClazz.isAbstract() || eClazz.getInstanceClass() == null) {
return false;
}
return AssociationClassElement.class.isAssignableFrom(eClazz.getInstanceClass());
}
/**
* Get all contained elements of a given element.
*
* @param modelElement
* the model element
* @param includeTransientContainments
* true if transient containments should be included in the
* result
* @return a set of contained model elements
*/
public static Set<EObject> getAllContainedModelElements(EObject modelElement, boolean includeTransientContainments) {
return getAllContainedModelElements(modelElement, includeTransientContainments, false);
}
/**
* Get all contained elements of a given element.
*
* @param modelElement
* the model element
* @param includeTransientContainments
* true if transient containments should be included in the
* result
* @param ignoreSingletonDatatypes
* whether to ignore singleton datatypes like, for example,
* EString
* @return a set of contained model elements
*/
public static Set<EObject> getAllContainedModelElements(EObject modelElement, boolean includeTransientContainments,
boolean ignoreSingletonDatatypes) {
Set<EObject> result = new HashSet<EObject>();
for (EObject containee : modelElement.eContents()) {
if (!isSingleton(containee) || !containee.eContainingFeature().isTransient()
|| includeTransientContainments) {
Set<EObject> elements = getAllContainedModelElements(containee, includeTransientContainments);
result.add(containee);
result.addAll(elements);
}
}
return result;
}
/**
* Get the container of an EObject.
*
* @param modelElement
* the model element
* @return the container
*/
public static EObject getContainerModelElement(EObject modelElement) {
EObject container = modelElement.eContainer();
if (container == null) {
return null;
}
if (EcoreFactory.eINSTANCE.getEcorePackage().getEObject().isInstance(container)) {
return container;
}
return null;
}
/**
* Get all contained elements of a given element as a list.
*
* @param modelElement
* the model element
* @param includeTransientContainments
* true if transient containments should be included in the
* result
* @return a list of contained model elements
*/
public static List<EObject> getAllContainedModelElementsAsList(EObject modelElement,
boolean includeTransientContainments) {
TreeIterator<EObject> it = modelElement.eAllContents();
List<EObject> result = new ArrayList<EObject>();
while (it.hasNext()) {
EObject containee = it.next();
if (containee.eContainingFeature() != null && !containee.eContainingFeature().isTransient()
|| includeTransientContainments) {
List<EObject> elements = getAllContainedModelElementsAsList(containee, includeTransientContainments);
if (!result.contains(containee)) {
result.add(containee);
}
result.addAll(elements);
}
}
return result;
}
/**
* Delete all incoming cross references to the given model element from any
* other model element in the given project.
*
* @param modelElement
* the model element
*/
public static void deleteIncomingCrossReferencesFromParent(Collection<Setting> inverseReferences,
EObject modelElement) {
for (Setting setting : inverseReferences) {
EStructuralFeature eStructuralFeature = setting.getEStructuralFeature();
EReference reference = (EReference) eStructuralFeature;
if (reference.isContainer() || reference.isContainment() || !reference.isChangeable()) {
continue;
}
EObject opposite = setting.getEObject();
if (eStructuralFeature.isMany()) {
((EList<?>) opposite.eGet(eStructuralFeature)).remove(modelElement);
} else {
opposite.eUnset(eStructuralFeature);
}
}
}
/**
* Check of the two elements are linked by the given reference.
*
* @param modelElement
* first model element
* @param otherModelElement
* second modelelement
* @param reference
* candidate reference feature of first element
* @return true if the reference feature links the two elements
*/
private static boolean isCorrespondingReference(EObject modelElement, EObject otherModelElement,
EReference reference) {
if (reference.isMany()) {
if (otherModelElement.eGet(reference) == null) {
return false;
}
return ((List<?>) otherModelElement.eGet(reference)).contains(modelElement);
} else {
return modelElement.equals(otherModelElement.eGet(reference));
}
}
/**
* Delete all outgoing cross references of the given model element.
*
* @param modelElement
* the model element
*/
public static void deleteOutgoingCrossReferences(IdEObjectCollection collection, EObject modelElement) {
Set<EObject> allModelElements = new HashSet<EObject>();
allModelElements.add(modelElement);
allModelElements.addAll(ModelUtil.getAllContainedModelElements(modelElement, false));
List<SettingWithReferencedElement> crossReferences = collectOutgoingCrossReferences(collection,
allModelElements);
for (SettingWithReferencedElement settingWithReferencedElement : crossReferences) {
Setting setting = settingWithReferencedElement.getSetting();
if (!settingWithReferencedElement.getSetting().getEStructuralFeature().isMany()) {
setting.getEObject().eUnset(setting.getEStructuralFeature());
} else {
List<EObject> references = (List<EObject>) setting.getEObject().eGet(setting.getEStructuralFeature());
references.remove(settingWithReferencedElement.getReferencedElement());
}
}
}
public static List<SettingWithReferencedElement> collectOutgoingCrossReferences(IdEObjectCollection collection,
Set<EObject> allModelElements) {
// result object
List<SettingWithReferencedElement> settings = new ArrayList<SettingWithReferencedElement>();
for (EObject currentElement : allModelElements) {
for (EReference reference : currentElement.eClass().getEAllReferences()) {
EClassifier eType = reference.getEType();
// sanity checks
if (reference.isContainer() || reference.isContainment() || !reference.isChangeable()
|| (!(eType instanceof EClass))) {
continue;
}
Setting setting = ((InternalEObject) currentElement).eSetting(reference);
// multi references
if (reference.isMany()) {
@SuppressWarnings("unchecked")
List<EObject> referencedElements = (List<EObject>) currentElement.eGet(reference);
for (EObject referencedElement : referencedElements) {
if (shouldBeCollected(collection, allModelElements, referencedElement)) {
settings.add(new SettingWithReferencedElement(setting, referencedElement));
}
}
} else {
// single references
EObject referencedElement = (EObject) currentElement.eGet(reference);
if (shouldBeCollected(collection, allModelElements, referencedElement)) {
settings.add(new SettingWithReferencedElement(setting, referencedElement));
}
}
}
}
return settings;
}
public static boolean shouldBeCollected(IdEObjectCollection collection, Set<EObject> allModelElements,
EObject referencedElement) {
if (referencedElement == null || !collection.containsInstance(referencedElement)) {
return false;
}
return !ModelUtil.isSingleton(referencedElement) && !ModelUtil.isIgnoredDatatype(referencedElement)
&& !allModelElements.contains(referencedElement);
}
/**
* Get the singleton instance for a given model element id.
*
* @param singletonId
* the id
* @return the singleton instance
*
* @see org.eclipse.emf.emfstore.common.model.SingletonIdResolver#getSingleton(org.eclipse.emf.emfstore.common.model.ModelElementId)
*/
public static EObject getSingleton(ModelElementId singletonId) {
initSingletonIdResolvers();
for (SingletonIdResolver resolver : singletonIdResolvers) {
EObject singleton = resolver.getSingleton(singletonId);
if (singleton != null) {
return singleton;
}
}
return null;
}
/**
* Get the singleton id for a singleton instance.
*
* @param singleton
* the singleton
* @return the id
*
* @see org.eclipse.emf.emfstore.common.model.SingletonIdResolver#getSingletonModelElementId(org.eclipse.emf.ecore.EObject)
*/
public static ModelElementId getSingletonModelElementId(EObject singleton) {
initSingletonIdResolvers();
for (SingletonIdResolver resolver : singletonIdResolvers) {
ModelElementId id = resolver.getSingletonModelElementId(singleton);
if (id != null) {
return clone(id);
}
}
return null;
}
/**
* Return whether the given eObject instance is a singelton.
*
* @param eObject
* the instance
* @return true if it is a singleton
*
* @see org.eclipse.emf.emfstore.common.model.SingletonIdResolver#isSingleton(org.eclipse.emf.ecore.EObject)
*/
public static boolean isSingleton(EObject eObject) {
initSingletonIdResolvers();
for (SingletonIdResolver resolver : singletonIdResolvers) {
if (resolver.isSingleton(eObject)) {
return true;
}
}
return false;
}
/**
* Initializes all available {@link SingletonIdResolver}.
*/
private static void initSingletonIdResolvers() {
if (singletonIdResolvers == null) {
// collect singleton ID resolvers
singletonIdResolvers = new HashSet<SingletonIdResolver>();
IConfigurationElement[] config = Platform.getExtensionRegistry().getConfigurationElementsFor(
"org.eclipse.emf.emfstore.common.model.singletonidresolver");
for (IConfigurationElement extension : config) {
SingletonIdResolver resolver;
try {
resolver = (SingletonIdResolver) extension.createExecutableExtension("class");
singletonIdResolvers.add(resolver);
} catch (CoreException e) {
ModelUtil.logWarning("Couldn't instantiate Singleton ID resolver:" + e.getMessage());
}
}
}
}
/**
* Checks whether any element within the given {@link Project} has any cross-references to the given {@link EObject}
* .
*
* @param element a {@link EObject}
* @param project a {@link Project}
* @param cutOffReferences whether cross-references should get cut off
* @return true, if there are any cross-references to the {@link EObject} within the given {@link Project}
*/
public static boolean handleIncomingCrossReferences(EObject element, Project project, boolean cutOffReferences) {
Set<EObject> projectContents = project.getAllModelElements();
Set<EObject> elementEObjects = getAllContainedModelElements(element, false);
elementEObjects.add(element);
for (EObject eObject : projectContents) {
@SuppressWarnings("rawtypes")
EContentsEList.FeatureIterator featureIterator = (EContentsEList.FeatureIterator) eObject
.eCrossReferences().iterator();
while (featureIterator.hasNext()) {
EObject referencedObject = (EObject) featureIterator.next();
if (elementEObjects.contains(referencedObject)) {
ModelElementId referencedObjectId = project.getModelElementId(referencedObject) == null ? project
.getDeletedModelElementId(referencedObject) : project.getModelElementId(referencedObject);
ModelElementId elementId = project.getModelElementId(element) == null ? project
.getDeletedModelElementId(element) : project.getModelElementId(element);
if (cutOffReferences) {
EReference eReference = (EReference) featureIterator.feature();
if (eReference.isMany()) {
@SuppressWarnings("unchecked")
List<EObject> eObjects = (List<EObject>) eObject.eGet(eReference);
eObjects.remove(referencedObject);
logInfo("Cross-reference " + referencedObjectId + " removed from many reference "
+ eReference + ".");
} else {
eObject.eUnset(eReference);
logInfo("Cross-reference " + referencedObject + " removed from " + eReference + ".");
}
} else {
throw new IllegalStateException("Cross-reference from " + referencedObject + " to " + elementId
+ " detected.");
}
}
}
}
return false;
}
public static Map<EObject, ModelElementId> copyModelElement(EObject originalObject, EObject copiedObject) {
Map<EObject, ModelElementId> idMap = new HashMap<EObject, ModelElementId>();
Project project = getProject(originalObject);
if (project == null) {
throw new IllegalArgumentException("EObject is not contained in a project.");
}
List<EObject> allContainedModelElements = ModelUtil.getAllContainedModelElementsAsList(originalObject, false);
allContainedModelElements.add(originalObject);
// EObject copiedElement = EcoreUtil.copy(originalObject);
List<EObject> copiedAllContainedModelElements = ModelUtil.getAllContainedModelElementsAsList(copiedObject,
false);
copiedAllContainedModelElements.add(copiedObject);
for (int i = 0; i < allContainedModelElements.size(); i++) {
EObject child = allContainedModelElements.get(i);
EObject copiedChild = copiedAllContainedModelElements.get(i);
ModelElementId childId = ModelUtil.clone(project.getModelElementId(child));
if (ModelUtil.isIgnoredDatatype(child)) {
continue;
}
idMap.put(copiedChild, childId);
}
return idMap;
}
}