blob: cf07b5e5207aaa414ec4c7e4d6cc2444d6f114f5 [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2004 IBM Corporation 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:
* IBM Corporation - initial API and implementation
****************************************************************************/
package org.eclipse.gmf.runtime.emf.core.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.emf.common.util.EMap;
import org.eclipse.emf.ecore.EAnnotation;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EModelElement;
import org.eclipse.emf.ecore.EObject;
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.util.EcoreUtil;
import org.eclipse.gmf.runtime.common.core.util.Trace;
import org.eclipse.gmf.runtime.emf.clipboard.core.ClipboardUtil;
import org.eclipse.gmf.runtime.emf.core.edit.MEditingDomain;
import org.eclipse.gmf.runtime.emf.core.edit.MObjectState;
import org.eclipse.gmf.runtime.emf.core.edit.MObjectType;
import org.eclipse.gmf.runtime.emf.core.edit.MRunnable;
import org.eclipse.gmf.runtime.emf.core.exceptions.MSLRuntimeException;
import org.eclipse.gmf.runtime.emf.core.internal.domain.MSLEditingDomain;
import org.eclipse.gmf.runtime.emf.core.internal.index.MSLReferenceVisitor;
import org.eclipse.gmf.runtime.emf.core.internal.plugin.MSLDebugOptions;
import org.eclipse.gmf.runtime.emf.core.internal.plugin.MSLPlugin;
import org.eclipse.gmf.runtime.emf.core.internal.resources.MResource;
import org.eclipse.gmf.runtime.emf.core.internal.util.MSLConstants;
import org.eclipse.gmf.runtime.emf.core.internal.util.MSLUtil;
/**
* This class contains a set of <code>EObject</code> related utility methods.
*
* @author rafikj
*/
public class EObjectUtil {
/**
* Creates an <code>EObject</code> given its <code>EClass</code>. The
* object is detached.
*
* @param eClass
* The <code>EClass</code>.
* @return The new <code>EObject</code>.
*/
public static EObject create(EClass eClass) {
return MEditingDomain.INSTANCE.create(eClass);
}
/**
* Creates an object at a given containment reference whose
* <code>EClass</code> is eClass.
*
* @param container
* The container of the new object.
* @param reference
* The feature containing the object.
* @param eClass
* The <code>EClass</code>.
* @return The new <code>EObject</code>.
*/
public static EObject create(EObject container, EReference reference,
EClass eClass) {
EObject eObject = null;
Resource resource = container.eResource();
MSLEditingDomain domain = null;
if (resource != null)
domain = (MSLEditingDomain) MEditingDomain
.getEditingDomain(resource);
if (domain == null)
domain = (MSLEditingDomain) MEditingDomain.INSTANCE;
if (resource instanceof MResource) {
eObject = ((MResource) resource).getHelper().create(domain, eClass);
MSLUtil.sendCreateEvent(domain, eObject);
} else
eObject = MSLUtil.create(domain, eClass, true);
if (reference.isMany())
((Collection) container.eGet(reference)).add(eObject);
else
container.eSet(reference, eObject);
return eObject;
}
/**
* Creates an object at a given containment reference whose
* <code>EClass</code> is eClass and assigns it a name.
*
* @param container
* The container of the new object.
* @param reference
* The feature containing the object.
* @param eClass
* the <code>EClass</code>.
* @param name
* The name.
* @return The new <code>EObject</code>.
*/
public static EObject create(EObject container, EReference reference,
EClass eClass, String name) {
EObject eObject = create(container, reference, eClass);
setName(eObject, name);
return eObject;
}
/**
* Detaches the object from its container.
*
* @param eObject
* The object to detach.
*/
public static void detach(EObject eObject) {
EObject container = eObject.eContainer();
if (container != null) {
EReference reference = eObject.eContainmentFeature();
if (reference.isMany())
((Collection) container.eGet(reference)).remove(eObject);
else
container.eSet(reference, null);
}
}
/**
* Destroys the object by removing it from its container and severing all
* references from and to the object. it is also recursive, i.e., it gets
* called on the children. Calling destroy on an already detached object
* will do nothing.
*
* @param eObject
* The object to destroy.
*/
public static void destroy(EObject eObject) {
EObject container = eObject.eContainer();
if (container != null) {
Resource resource = container.eResource();
MSLEditingDomain domain = null;
if (resource != null)
domain = (MSLEditingDomain) MEditingDomain
.getEditingDomain(resource);
if (domain == null)
domain = (MSLEditingDomain) MEditingDomain.INSTANCE;
if (resource instanceof MResource)
((MResource) resource).getHelper().destroy(domain, eObject);
else
MSLUtil.destroy(domain, eObject, 0);
}
}
/**
* Gets the objectÂ’s state which could be either: <code>CLOSED</code>:
* the object is a proxy. <code>OPEN</code>: the object belongs to a
* resource. <code>DETACHED</code>: the object does not belong to a
* resource.
*
* @param eObject
* The object.
* @return The object's state.
*/
public static MObjectState getState(EObject eObject) {
if (eObject.eIsProxy())
return MObjectState.CLOSED;
else if (eObject.eResource() == null)
return MObjectState.DETACHED;
else
return MObjectState.OPEN;
}
/**
* Gets the objectÂ’s type which could be either: <code>MODELING</code>:
* the object is a modeling object.
*
* @param eObject
* The object.
* @return The object's type.
*/
public static MObjectType getType(EObject eObject) {
Resource resource = eObject.eResource();
if (resource instanceof MResource)
return ((MResource) resource).getHelper().getType();
return MObjectType.MODELING;
}
/**
* Gets the ID of an object. the object must be part of a
* <code>XMLResource</code> that supports IDs.
*
* @param eObject
* The object.
* @return The ID of the object.
*/
public static String getID(EObject eObject) {
if (eObject.eIsProxy())
return ProxyUtil.getProxyID(eObject);
Resource resource = eObject.eResource();
String id = null;
if (resource instanceof MResource)
id = ((MResource) resource).getHelper().getID(eObject);
else
id = MSLUtil.getID(eObject);
if (id != null)
return id;
else
return MSLConstants.EMPTY_STRING;
}
/**
* Sets the ID of an object. Object IDs are automatically generated when
* objects are created. This method should be called only to modify the ID
* of an object. the object must be part of a <code>XMLResource</code>
* that supports IDs.
*
* @param eObject
* The object.
* @param id
* The new object's ID.
*/
public static void setID(EObject eObject, String id) {
Resource resource = eObject.eResource();
if (resource != null) {
if (resource instanceof MResource)
((MResource) resource).getHelper().setID(eObject, id);
else
MSLUtil.setID(eObject, id);
}
}
/**
* Gets the name of an object if the object has name, returns an empty
* string otherwise.
*
* @param eObject
* The object.
* @return The object's name.
*/
public static String getName(EObject eObject) {
if (eObject.eIsProxy())
return ProxyUtil.getProxyName(eObject);
EAttribute nameAttribute = MetaModelUtil.getNameAttribute(eObject
.eClass());
if (nameAttribute != null) {
String name = (String) eObject.eGet(nameAttribute);
if (name != null)
return name;
}
return MSLConstants.EMPTY_STRING;
}
/**
* Sets the name of an object if the object can be assigned a name.
*
* @param eObject
* The object.
* @param name
* The object's new name.
* @return True if the name was set.
*/
public static boolean setName(EObject eObject, String name) {
EAttribute nameAttribute = MetaModelUtil.getNameAttribute(eObject
.eClass());
if (nameAttribute != null) {
eObject.eSet(nameAttribute, name);
return true;
} else {
RuntimeException e = new IllegalArgumentException(
"object has no name attribute"); //$NON-NLS-1$
Trace.throwing(MSLPlugin.getDefault(),
MSLDebugOptions.EXCEPTIONS_THROWING, EObjectUtil.class,
"setName", e); //$NON-NLS-1$
throw e;
}
}
/**
* Gets the fully qualified name of an object.
*
* @param eObject
* The object.
* @param formatted
* if True, unnamed parents will be listed using their meta-class
* name.
* @return The object's qualified name.
*/
public static String getQName(EObject eObject, boolean formatted) {
if (eObject.eIsProxy())
return ProxyUtil.getProxyQName(eObject);
if (!formatted) {
EAttribute qNameAttribute = MetaModelUtil.getQNameAttribute(eObject
.eClass());
if (qNameAttribute != null) {
String qualifiedName = (String) eObject.eGet(qNameAttribute);
if (qualifiedName != null)
return qualifiedName;
else
return MSLConstants.EMPTY_STRING;
}
}
String prefix = MSLConstants.EMPTY_STRING;
EObject eContainer = eObject.eContainer();
while ((eContainer != null) && (eContainer instanceof EAnnotation))
eContainer = eContainer.eContainer();
if (eContainer != null)
prefix = getQName(eContainer, formatted);
String name = getName(eObject);
if ((formatted) && (name.equals(MSLConstants.EMPTY_STRING)))
name = MSLConstants.META_CLASS_BEGIN
+ MetaModelUtil.getLocalName(eObject.eClass())
+ MSLConstants.META_CLASS_END;
return (prefix.length() == 0) ? name
: (prefix + ((name.equals(MSLConstants.EMPTY_STRING)) ? MSLConstants.EMPTY_STRING
: (MSLConstants.QUALIFIED_NAME_SEPARATOR + name)));
}
/**
* Gets the file of the eObject if any.
*
* @param eObject
* The eObject
* @return The file path of the eObject's resource (or null if no resource)
*/
public static IFile getWorkspaceFile(EObject eObject) {
Resource resource = eObject.eResource();
if (resource != null)
return ResourceUtil.getFile(resource);
return null;
}
/**
* Checks if an object is modifiable. An object is modifiable only if its
* resource is modifiable or the object does not belong to any resource.
*
* @param eObject
* The object.
* @return True if object is modifiable.
*/
public static boolean isModifiable(EObject eObject) {
Resource resource = eObject.eResource();
if (resource != null)
return ResourceUtil.isModifiable(resource);
return true;
}
/**
* Gets the first container with the specified EClass.
*
* @param eObject
* The <code>EObject</code>.
* @param eClass
* The eClass.
* @return The container.
*/
public static EObject getContainer(EObject eObject, EClass eClass) {
for (EObject parent = eObject; parent != null; parent = parent
.eContainer())
if (eClass.isInstance(parent))
return parent;
return null;
}
/**
* Checks if an object is contained recursively by another object.
*
* @param container
* The container.
* @param eObject
* The contained.
* @return True if contains, false otherwise.
*/
public static boolean contains(EObject container, EObject eObject) {
if (container == eObject)
return true;
else if ((container == null) || (eObject == null))
return false;
else
return contains(container, eObject.eContainer());
}
/**
* Metamorphoses an object. The object will be replaced by the newly created
* object of the given type. The contents of the object will moved to the
* new object and all references to the object will redirected to the new
* object.
*
* @param eObject
* The <code>EObject</code>.
* @param newClass
* The new <code>EClass</code>.
* @return The new metamorphosed object.
*/
public static EObject metamorphose(EObject eObject, final EClass newClass) {
EObject eContainer = eObject.eContainer();
EClass eClass = eObject.eClass();
EReference containmentFeature = eObject.eContainmentFeature();
int containmentPosition = -1;
List containmentList = null;
if (containmentFeature != null && containmentFeature.isMany()) {
containmentList = (List) eContainer.eGet(containmentFeature);
containmentPosition = containmentList.indexOf(eObject);
}
Resource resource = eObject.eResource();
MSLEditingDomain domain = null;
if (resource != null)
domain = (MSLEditingDomain) MEditingDomain
.getEditingDomain(resource);
if (domain == null)
domain = (MSLEditingDomain) MEditingDomain.INSTANCE;
final EObject newObject;
if (resource instanceof MResource)
newObject = ((MResource) resource).getHelper().create(domain,
newClass);
else
newObject = domain.create(newClass);
// The object we will copy from.
EObject copyObject = ((eContainer != null) && (canContain(eContainer,
newClass, false))) ? eObject
: EcoreUtil.copy(eObject);
// Clone the features.
{
List allFeatures = eClass.getEAllStructuralFeatures();
List allNewFeatures = newClass.getEAllStructuralFeatures();
Iterator i = allFeatures.iterator();
while (i.hasNext()) {
EStructuralFeature feature = (EStructuralFeature) i.next();
if ((!feature.isChangeable()) || (feature.isDerived()))
continue;
if ((feature instanceof EReference)
&& (((EReference) feature).isContainer()))
continue;
// Find the target feature.
EStructuralFeature newFeature = null;
if (allNewFeatures.contains(feature))
newFeature = feature;
else {
newFeature = newClass.getEStructuralFeature(feature
.getName());
if ((newFeature != null)
&& (newFeature.getEType() != feature.getEType()))
newFeature = null;
}
// Copy the feature value.
if (newFeature != null) {
if (newFeature.isMany()) {
Object[] list = ((Collection) copyObject.eGet(feature))
.toArray();
Collection newList = (Collection) newObject
.eGet(newFeature);
for (int j = 0; j < list.length; j++) {
Object object = (EObject) list[j];
newList.add(object);
}
} else {
Object object = copyObject.eGet(feature);
if (object != null)
newObject.eSet(newFeature, object);
}
}
}
}
// Replace the references to eObject with newObject.
{
if (copyObject == eObject) {
MSLReferenceVisitor visitor = new MSLReferenceVisitor(domain,
eObject) {
protected void visitedReferencer(EReference reference,
EObject referencer) {
MSLUtil.fixReferences(referencedObject, newClass,
newObject, reference,
(EClass) reference.getEType(), referencer);
}
};
visitor.visitReferencers();
// Replace old object with new object in the container.
if (containmentFeature != null && containmentFeature.isMany()) {
destroy(eObject);
containmentList.add(containmentPosition, newObject);
} else if (containmentFeature != null) {
eContainer.eSet(containmentFeature, newObject);
}
}
}
return newObject;
}
/**
* Uses a reverse reference map that is maintained by the MSL service to
* find all referencers of a particular element. The search can be narrowed
* down by passing the list of Reference features to match.
* <code>features</code> can be null.
*
* @param eObject
* The referenced object.
* @param features
* The reference features.
* @return The collection of referencers.
*/
public static Collection getReferencers(EObject eObject,
EReference[] features) {
Resource resource = eObject.eResource();
MSLEditingDomain domain = null;
if (resource != null)
domain = (MSLEditingDomain) MEditingDomain
.getEditingDomain(resource);
if (domain == null)
domain = (MSLEditingDomain) MEditingDomain.INSTANCE;
if ((features != null) && (features.length != 0)) {
Collection referencers = new ArrayList();
for (int i = 0, count = features.length; i < count; i++) {
EReference feature = features[i];
Iterator j = domain.getObjectIndexer().getReferencers(eObject,
feature).iterator();
while (j.hasNext()) {
EObject referencer = (EObject) j.next();
referencers.add(referencer);
}
}
return referencers;
} else
return domain.getObjectIndexer().getAllReferencers(eObject);
}
/**
* Replaces all of the references to <code>referencedElement</code> with
* <code>replacement</code>.
*
* @param referenced
* the referenced element to be replaced
* @param replacement
* the replacement element
* @param ignoredObjects
* ignore references in these elements and their children. Can be
* an empty list.
*/
public static void replaceReferencesTo(final EObject referenced,
final EObject replacement, final List ignoredObjects) {
Resource resource = referenced.eResource();
MSLEditingDomain domain = null;
if (resource != null)
domain = (MSLEditingDomain) MEditingDomain
.getEditingDomain(resource);
if (domain == null)
domain = (MSLEditingDomain) MEditingDomain.INSTANCE;
MSLReferenceVisitor visitor = new MSLReferenceVisitor(domain,
referenced) {
protected void visitedReferencer(EReference reference,
EObject referencer) {
MSLUtil.replaceReferences(referenced, replacement,
ignoredObjects, reference, referencer);
}
};
visitor.visitReferencers();
}
/**
* Checks if the object can contain an object of type eClass. The check can
* be recursive.
*
* @param container
* The container.
* @param eClass
* The <code>EClass</code>.
* @param recursive
* True if recursive.
* @return True if an object can contain other objects of a given type.
*/
public static boolean canContain(EObject container, EClass eClass,
boolean recursive) {
return MetaModelUtil.canContain(container.eClass(), eClass, recursive);
}
/**
* Checks if the object can contain an object of type eClass at a given
* containment reference. The check can be recursive.
*
* @param container
* The container.
* @param reference
* The feature.
* @param eClass
* The <code>EClass</code>.
* @param recursive
* True if recursive.
* @return True if an object can contain other objects of a given type.
*/
public static boolean canContain(EObject container, EReference reference,
EClass eClass, boolean recursive) {
return MetaModelUtil.canContain(container.eClass(), reference, eClass,
recursive);
}
/**
* Checks if the object can reference an object of type eClass.
*
* @param container
* The reference container.
* @param eClass
* The <code>EClass</code>.
* @return True if an object can reference other objects of a given type.
*/
public static boolean canReference(EObject container, EClass eClass) {
return MetaModelUtil.canReference(container.eClass(), eClass);
}
/**
* Checks if the object can reference an object of type eClass at a given
* non containment reference.
*
* @param container
* The reference container.
* @param reference
* The feature.
* @param eClass
* The <code>EClass</code>.
* @return True if an object can reference other objects of a given type.
*/
public static boolean canReference(EObject container, EReference reference,
EClass eClass) {
return MetaModelUtil
.canReference(container.eClass(), reference, eClass);
}
/**
* Creates an annotation.
*
* @param container
* The annotation container.
* @param source
* The annotation source.
* @return The new annotation.
*/
public static EAnnotation createEAnnotation(EModelElement container,
String source) {
EAnnotation annotation = (EAnnotation) create(container,
EcorePackage.eINSTANCE.getEModelElement_EAnnotations(),
EcorePackage.eINSTANCE.getEAnnotation());
annotation.setSource(source);
return annotation;
}
/**
* Adds an annotation.
*
* @param container
* The annotation container.
* @param annotation
* The annotation to add.
*/
public static void addEAnnotation(EModelElement container,
EAnnotation annotation) {
container.getEAnnotations().add(annotation);
}
/**
* Removes an annotation.
*
* @param container
* The annotation container.
* @param source
* The annotation source.
* @return The removed annotation.
*/
public static EAnnotation detachEAnnotation(EModelElement container,
String source) {
EAnnotation annotation = container.getEAnnotation(source);
if (annotation != null)
detach(annotation);
return annotation;
}
/**
* Destroys an annotation.
*
* @param container
* The annotation container.
* @param source
* The annotation source.
* @return True if an annotation was destroyed..
*/
public static boolean destroyEAnnotation(EModelElement container,
String source) {
EAnnotation annotation = container.getEAnnotation(source);
if (annotation != null)
destroy(annotation);
return (annotation != null);
}
/**
* Adds or sets a detail entry given a key/value pair. It returns the
* previous value if any.
*
* @param annotation
* The annotation.
* @param key
* The key.
* @param value
* The value.
* @return The previous value.
*/
public static String putDetail(EAnnotation annotation, String key,
String value) {
EMap details = annotation.getDetails();
String oldValue = (String) details.get(key);
details.put(key, value);
return oldValue;
}
/**
* Gets the detail value given a key.
*
* @param annotation
* The annotation.
* @param key
* The key.
* @return The value.
*/
public static String getDetail(EAnnotation annotation, String key) {
return (String) annotation.getDetails().get(key);
}
/**
* Copies a collection of objects to a string. Hints are used to customize
* the operation.
*
* @param objects
* The objects to copy.
* @param hints
* Hints used during copy. This an object to string map.
* @return The serialized output.
*/
public static String serialize(Collection objects, Map hints) {
try {
return ClipboardUtil.copyElementsToString(
objects, hints, new NullProgressMonitor());
} catch (Exception ex) {
handleException(ex, "copySelectionToString"); //$NON-NLS-1$
}
return null;
}
/**
* Pastes a collection of objects from a string. Hints are used to customize
* the operation
*
* @param target
* The target of the copy.
* @param str
* The string to deserialize.
* @param hints
* Hints used during paste.
* @return The collection of pasted objects.
*/
public static Collection deserialize(final EObject target, final String str, final Map hints) {
return (Collection) OperationUtil.runWithNoSemProcs(new MRunnable() {
public Object run() {
try {
return ClipboardUtil.pasteElementsFromString(
str, target, hints, new NullProgressMonitor());
} catch (Exception ex) {
handleException(ex, "deserialize"); //$NON-NLS-1$
}
return null;
}
});
}
/**
* Pastes a collection of objects from a string. Hints are used to customize
* the operation
*
* @param target
* The target of the copy.
* @param str
* The string to deserialize.
* @param hints
* Hints used during paste.
* @return The collection of pasted objects.
*/
public static Collection deserialize(final Resource target, final String str, final Map hints) {
return (Collection) OperationUtil.runWithNoSemProcs(new MRunnable() {
public Object run() {
try {
return ClipboardUtil.pasteElementsFromString(
str, target, hints, new NullProgressMonitor());
} catch (Exception ex) {
handleException(ex, "deserialize"); //$NON-NLS-1$
}
return null;
}
});
}
/**
* Finds the first common container of a collection of objects.
*
* @param objects
* The <code>EObject</code>s.
* @param desiredContainerType
* The desired <code>EClass</code> of the container.
* @return The least common container.
*/
public static EObject getLeastCommonContainer(Collection objects,
EClass desiredContainerType) {
EObject commonContainer = null;
List prevContainers = new ArrayList();
Iterator i = objects.iterator();
while (i.hasNext()) {
EObject element = (EObject) i.next();
List containers = new ArrayList();
boolean found = false;
EObject container = element;
while (container != null) {
EClass containerClass = container.eClass();
if ((desiredContainerType == containerClass)
|| (desiredContainerType.isSuperTypeOf(containerClass))) {
containers.add(container);
if (!found) {
if ((prevContainers.isEmpty())
|| (commonContainer == null)) {
commonContainer = container;
found = true;
} else if ((prevContainers.contains(container))
&& (contains(container, commonContainer))) {
commonContainer = container;
found = true;
}
}
}
container = container.eContainer();
}
if (!found)
return null;
prevContainers = containers;
}
return commonContainer;
}
/**
* Gets the string representing the full path for the model for an element.
*
* @param eObject
* the element.
* @return the model path name.
*/
public static String getFileString(EObject eObject) {
return ResourceUtil.getFileString(eObject.eResource());
}
/**
* Return the file in the workspace for a element.
*
* @param eObject
* the element.
* @return the file in the workspace for a element.
*/
public static IFile getFile(EObject eObject) {
if (eObject.eResource() == null) {
return null;
} else {
return ResourceUtil.getFile(eObject.eResource());
}
}
private EObjectUtil() {
// private
}
/**
* handles the exception, does tracing ...etc.
*
* @param ex
* the exception to hanlde
* @param methodname
* the calling method
*/
private static void handleException(Exception ex, String methodname) {
if (ex instanceof OperationCanceledException) {
Trace.catching(MSLPlugin.getDefault(),
MSLDebugOptions.EXCEPTIONS_CATCHING, EObjectUtil.class,
methodname, ex);
} else {
Trace.throwing(MSLPlugin.getDefault(),
MSLDebugOptions.EXCEPTIONS_THROWING, EObjectUtil.class,
methodname, ex);
throw (ex instanceof MSLRuntimeException) ? (MSLRuntimeException) ex
: new MSLRuntimeException("EObjectUtil General Error", ex);//$NON-NLS-1$
}
}
}