| /****************************************************************************** |
| * Copyright (c) 2002, 2006 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.clipboard.core; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EAttribute; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.ENamedElement; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.InternalEObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.ExtendedMetaData; |
| import org.eclipse.emf.ecore.util.FeatureMapUtil; |
| import org.eclipse.emf.ecore.xmi.XMLResource; |
| import org.eclipse.gmf.runtime.emf.clipboard.core.internal.ClipboardPlugin; |
| |
| /** |
| * Utilities to assist the implementation of {@link IClipboardSupport}s by |
| * plug-ins extending the <tt>clipboardSupport</tt> extension point. |
| * |
| * @author Yasser Lulu |
| */ |
| public final class ClipboardSupportUtil { |
| /** Index in an array or list indicating absence of the element sought. */ |
| public static final int NONE = -1; |
| |
| private static final String ONE = "1"; //$NON-NLS-1$ |
| |
| private static final String UNDERSCORE = "_"; //$NON-NLS-1$ |
| |
| private static final String RESOLVE = "resolve"; //$NON-NLS-1$ |
| |
| private static final EReference[] EMPTY_REF_ARRAY = new EReference[0]; |
| |
| /** |
| * Not instantiable by clients. |
| */ |
| private ClipboardSupportUtil() { |
| super(); |
| } |
| |
| /** |
| * Removes from a collection of <code>elements</code> any elements that |
| * should not be copied. These are those that have containers that are |
| * already in the <code>elements</code> collection. |
| * |
| * @param elements the collection of elements to be whittled down to those |
| * that should be copied. <b>Note</b> that this collection is modified |
| * in place |
| * @return the <code>elements</code> collection, again |
| */ |
| public static Collection getCopyElements(Collection elements) { |
| Iterator it = elements.iterator(); |
| EObject element = null; |
| Set set = new HashSet(); |
| while (it.hasNext()) { |
| element = (EObject) it.next(); |
| set.add(element); |
| } |
| elements.clear(); |
| elements.addAll(getUniqueElementsAncestry(set)); |
| return elements; |
| } |
| |
| /** |
| * Finds the subset of a set of elements that do not have ancestors in that |
| * set. |
| * |
| * @param elementSet a set of {@link EObject}s |
| * @return the subset of the elements that are not contained within others |
| * in the set |
| */ |
| private static Set getUniqueElementsAncestry(Set elementSet) { |
| Iterator it = elementSet.iterator(); |
| EObject container = null; |
| while (it.hasNext()) { |
| container = ((EObject) it.next()).eContainer(); |
| while (container != null) { |
| if (elementSet.contains(container)) { |
| it.remove(); |
| break; |
| } |
| container = container.eContainer(); |
| } |
| } |
| return elementSet; |
| } |
| |
| /** |
| * Replaces an object's many reference with an entirely new list of |
| * references. Does nothing if the <code>reference</code> specified is |
| * not {@linkplain #isOkToSetEList(EObject, EReference) settable}. |
| * |
| * @param eObject the element owning the reference to be set |
| * @param reference the many reference to be set |
| * @param referencedObjects the new value of the reference; must be an |
| * {@link org.eclipse.emf.common.util.EList} of {@link EObject}s |
| * |
| * @return the <code>referencedObjects</code> |
| * |
| * @see #isOkToSetEList(EObject, EReference) |
| */ |
| public static List setEObjectList(EObject eObject, |
| EReference reference, List referencedObjects) { |
| |
| if (isOkToSetEList(eObject, reference)) { |
| if (reference.isContainment()) { |
| for (Iterator referenced = referencedObjects.iterator();referenced.hasNext();) { |
| EObject referencedObject = (EObject)referenced.next(); |
| ((InternalEObject)referencedObject).eSetResource(null,null); |
| } |
| sendCreateEvent(referencedObjects); |
| } |
| eObject.eSet(reference, referencedObjects); |
| } |
| return referencedObjects; |
| } |
| |
| /** |
| * Queries whether a reference is mutable. A reference is considered |
| * mutable if and only if it is changeable and it is either not derived |
| * or it is a member of a feature map (though not itself a feature map). |
| * |
| * @param reference the reference to test |
| * |
| * @return <code>true</code> if the reference is mutable; |
| * <code>false</code>, otherwise |
| */ |
| static boolean isMutable(EReference reference) { |
| boolean result = reference.isChangeable(); |
| |
| if (result) { |
| if (reference.isDerived()) { |
| // check whether it is a feature-map member that is not, itself, |
| // a feature map |
| EStructuralFeature group = ExtendedMetaData.INSTANCE.getGroup(reference); |
| |
| result = (group != null) && !FeatureMapUtil.isFeatureMap(reference); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Queries whether a many reference may be replaced with an entirely new |
| * list of {@link EObject}s. |
| * |
| * @param eObject the owner of the <code>reference</code> |
| * @param reference a many reference to query whether it is settable |
| * @return <code>true</code> if the <code>reference</code> is changeable |
| * and is not derived; <code>false</code>, otherwise |
| */ |
| public static boolean isOkToSetEList(EObject eObject, EReference reference) { |
| if (isMutable(reference)) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Appends an object's many reference with a list of additional |
| * references. |
| * |
| * @param eObject the element owning the reference list to be appended |
| * @param reference the many reference to be appended |
| * @param referencedObjects {@link EObject}s to append to the list |
| * |
| * @return those of the <code>referencedObjects</code> that were |
| * successfully appended |
| * |
| * @see #appendEObjectAt(EObject, EReference, EObject) |
| */ |
| public static List appendEObjectListAt(EObject eObject, |
| EReference reference, List referencedObjects) { |
| List list = new ArrayList(); |
| EObject childEObject = null; |
| Iterator childIt = referencedObjects.iterator(); |
| while (childIt.hasNext()) { |
| childEObject = (EObject) childIt.next(); |
| childEObject = appendEObjectAt(eObject, reference, |
| childEObject); |
| if (childEObject != null) { |
| list.add(childEObject); |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Appends an object's many reference with an additional reference. |
| * Has no effect if this <code>reference</code> cannot be |
| * {@linkplain #isOkToAppendEObjectAt(EObject, EReference, EObject) appended}. |
| * |
| * @param eObject the element owning the reference list to be appended |
| * @param reference the many reference to be appended |
| * @param referencedObject an object to append to the list |
| * |
| * @return <code>referencedObject</code> if it was successfully appended |
| * to the reference; <code>null</code>, otherwise |
| * |
| * @see #appendEObjectAt(EObject, EReference, EObject) |
| * @see #isOkToAppendEObjectAt(EObject, EReference, EObject) |
| */ |
| public static EObject appendEObjectAt(EObject eObject, |
| EReference reference, EObject referencedObject) { |
| if (isOkToAppendEObjectAt(eObject, reference, referencedObject) == false) { |
| return null; |
| } |
| if (reference.isContainment()) { |
| ((InternalEObject)referencedObject).eSetResource(null,null); |
| sendCreateEvent(referencedObject); |
| } |
| ((Collection) eObject.eGet(reference)).add(referencedObject); |
| return referencedObject; |
| } |
| |
| /** |
| * Appends a resource's contents with an additional reference. |
| * |
| * @param resource the resource |
| * @param referencedObject an object to append to the list |
| * |
| * @return <code>referencedObject</code> if it was successfully appended |
| * to the resource; <code>null</code>, otherwise |
| * |
| */ |
| public static EObject appendEObject(Resource resource, EObject referencedObject) { |
| ((InternalEObject)referencedObject).eSetResource(null,null); |
| sendCreateEvent(referencedObject); |
| resource.getContents().add(referencedObject); |
| return referencedObject; |
| } |
| |
| /** |
| * Replaces an object's scalar reference with an new element. |
| * Does nothing if the <code>reference</code> specified is not |
| * {@linkplain #isOkToSetEObject(EObject, EReference, EObject) settable}. |
| * |
| * @param eObject the element owning the reference to be set |
| * @param reference the scalar reference to be set |
| * @param referencedObject the new value of the reference |
| * |
| * @return the <code>referencedObjects</code> |
| * |
| * @see #isOkToSetEObject(EObject, EReference, EObject) |
| */ |
| public static EObject setEObject(EObject eObject, |
| EReference reference, EObject referencedObject) { |
| if (isOkToSetEObject(eObject, reference, referencedObject) == false) { |
| return null; |
| } |
| if (reference.isContainment()) { |
| ((InternalEObject)referencedObject).eSetResource(null,null); |
| sendCreateEvent(referencedObject); |
| } |
| eObject.eSet(reference, referencedObject); |
| return referencedObject; |
| } |
| |
| /** |
| * Replaces an object's attribute with an new value. |
| * Does nothing if the <code>attribute</code> specified is not |
| * {@linkplain #isOkToSetEAttribute(EObject, EAttribute, Object) settable}. |
| * |
| * @param eObject the element owning the attribute to be set |
| * @param attribute the attribute to be set |
| * @param value the new value of the attribute |
| * |
| * @see #isOkToSetEAttribute(EObject, EAttribute, Object) |
| */ |
| public static void setEAttribute(EObject eObject, |
| EAttribute attribute, Object value) { |
| if (isOkToSetEAttribute(eObject, attribute, value)) { |
| eObject.eSet(attribute, value); |
| } |
| } |
| |
| private static IClipboardSupport createClipboardSupport(EObject eObject) { |
| return ClipboardUtil.createClipboardSupport(eObject.eClass()); |
| } |
| |
| /** |
| * Broadcasts a creatio notification for a new {@link EObject} |
| * via the appropriate clipboard support utility, if the metamodel support |
| * such notifications. |
| * |
| * @param eObject a newly created <code>EObject</code> |
| * |
| * @see IClipboardSupport#sendCreateNotification(EObject) |
| */ |
| public static void sendCreateEvent(EObject eObject) { |
| IClipboardSupport cSupport = createClipboardSupport(eObject); |
| |
| if (null != cSupport) |
| cSupport.sendCreateNotification(eObject); |
| } |
| |
| /** |
| * Broadcasts creation notifications for a list of new {@link EObject}s |
| * via the appropriate clipboard support utility, if the metamodel support |
| * such notifications. |
| * |
| * @param eObjects a list of newly created {@link EObject}s |
| * |
| * @see #sendCreateEvent(EObject) |
| */ |
| public static void sendCreateEvent(List eObjects) { |
| for (Iterator i = eObjects.iterator(); i.hasNext();) |
| sendCreateEvent((EObject) i.next()); |
| } |
| |
| /** |
| * Queries whether an attribute may be set to the specified new value. |
| * |
| * @param eObject the owner of the <code>attribute</code> |
| * @param attribute an attribute to query whether it is settable |
| * @param value the proposed new value of the <code>attribute</code> |
| * |
| * @return <code>true</code> if the <code>attribute</code> is changeable |
| * and the <code>value</code> is not <code>null</code>; |
| * <code>false</code>, otherwise |
| */ |
| public static boolean isOkToSetEAttribute(EObject eObject, |
| EAttribute attribute, Object value) { |
| if ((value != null) && (attribute != null)) { |
| return attribute.isChangeable(); |
| } |
| return false; |
| } |
| |
| /** |
| * Clears an object's scalar reference. |
| * Has no effect if this <code>reference</code> cannot be |
| * {@linkplain #isOkToDestroyEObject(EObject, EReference) cleared}. |
| * |
| * @param eObject the element owning the reference to be cleared |
| * @param reference the scalar reference to be cleared |
| * |
| * @see #isOkToDestroyEObject(EObject, EReference) |
| */ |
| public static void destroyEObject(EObject eObject, |
| EReference reference) { |
| if (isOkToDestroyEObject(eObject, reference) == false) { |
| return; |
| } |
| EObject current = (EObject) eObject.eGet(reference); |
| if (current == null) { |
| return; |
| } |
| if (reference.isContainment()) { |
| createClipboardSupport(current).destroy(current); |
| } else { |
| eObject.eSet(reference, null); |
| } |
| } |
| |
| /** |
| * Removes an element from an object's many reference. |
| * Has no effect if this <code>reference</code> cannot be |
| * {@linkplain #isOkToDestroyEObjectInCollection(EObject, EReference) removed}. |
| * |
| * @param eObject the element owning the reference list to be reduced |
| * @param reference the many reference to be reduced |
| * @param referencedObject an object to remove from the list |
| * |
| * @see #isOkToDestroyEObjectInCollection(EObject, EReference) |
| */ |
| public static void destroyEObjectInCollection(EObject eObject, |
| EReference reference, EObject referencedObject) { |
| if (isOkToDestroyEObjectInCollection(eObject, reference) == false) { |
| return; |
| } |
| if (reference.isContainment()) { |
| createClipboardSupport(referencedObject).destroy(referencedObject); |
| } else { |
| ((Collection) eObject.eGet(reference)).remove(referencedObject); |
| } |
| } |
| |
| /** |
| * Removes an element from a resource. |
| * |
| * @param referencedObject an object to remove from the resource |
| */ |
| public static void destroyEObjectInResource(EObject referencedObject) { |
| createClipboardSupport(referencedObject).destroy(referencedObject); |
| } |
| |
| /** |
| * Queries whether a many reference may be appended with an new |
| * {@link EObject}. |
| * |
| * @param eObject the owner of the <code>reference</code> |
| * @param reference a many reference to query whether it is appendable |
| * @param referencedObject an object that is proposed to be appended |
| * to the <code>reference</code> |
| * |
| * @return <code>true</code> if the <code>reference</code> is changeable |
| * and is not derived, and does not already contain the |
| * <code>referencedObject</code> or appending it would not violate |
| * the <code>reference</code>'s declared upper bound; |
| * <code>false</code>, otherwise |
| */ |
| public static boolean isOkToAppendEObjectAt(EObject eObject, |
| EReference reference, EObject referencedObject) { |
| if (isMutable(reference)) { |
| int lowerBound = reference.getLowerBound(); |
| int upperBound = reference.getUpperBound(); |
| if (lowerBound != upperBound) { |
| if (eObject.eIsSet(reference)) { |
| List list = (List) eObject.eGet(reference); |
| return (((upperBound == NONE) || (list.size() < upperBound)) && (list |
| .contains(referencedObject) == false)); |
| } else { |
| return ((upperBound == NONE) || (upperBound > 0)); |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Queries whether a scalar reference may be set to an new {@link EObject}. |
| * |
| * @param eObject the owner of the <code>reference</code> |
| * @param reference a scalar reference to query whether it is settable |
| * @param referencedObject an object that is proposed to be assigned |
| * to the <code>reference</code> |
| * |
| * @return <code>true</code> if the <code>reference</code> is changeable |
| * and is not derived, and does not already contain the |
| * <code>referencedObject</code> or the <code>referencedObject</code> |
| * is <code>null</code>; <code>false</code>, otherwise |
| */ |
| public static boolean isOkToSetEObject(EObject eObject, |
| EReference reference, EObject referencedObject) { |
| if (isMutable(reference)) { |
| Object value = eObject.eGet(reference); |
| return ((referencedObject != null) && (value != referencedObject)); |
| } |
| return false; |
| } |
| |
| /** |
| * Queries whether a scalar reference may be cleared. |
| * |
| * @param eObject the owner of the <code>reference</code> |
| * @param reference a scalar reference to query whether it is clearable |
| * |
| * @return <code>true</code> if the <code>reference</code> is changeable |
| * and its value is not already <code>null</code>; |
| * <code>false</code>, otherwise |
| */ |
| public static boolean isOkToDestroyEObject(EObject eObject, |
| EReference reference) { |
| if (reference.isChangeable()) { |
| Object value = eObject.eGet(reference); |
| return (value != null); |
| } |
| return false; |
| } |
| |
| /** |
| * Queries whether a many reference may have a reference removed from it. |
| * |
| * @param eObject the owner of the <code>reference</code> |
| * @param reference a many reference to query whether it is removable |
| * |
| * @return <code>true</code> if the <code>reference</code> is changeable |
| * and is not derived, and removing an element would not violate |
| * its declared lower bound; <code>false</code>, otherwise |
| */ |
| public static boolean isOkToDestroyEObjectInCollection(EObject eObject, |
| EReference reference) { |
| if (isMutable(reference)) { |
| int lowerBound = reference.getLowerBound(); |
| int upperBound = reference.getUpperBound(); |
| if ((lowerBound != upperBound) && (eObject.eIsSet(reference))) { |
| List list = (List) eObject.eGet(reference); |
| return (list.size() > lowerBound); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Finds an Ecore element from a list of <code>elements</code> whose having |
| * the specified <code>name</code>. If an exact match is not found, and |
| * <code>trySubNames</code> is <code>true</code>, then the match is |
| * attempted again by trying to find an element whose name is a superset or |
| * a subset of the <code>name</code>. |
| * |
| * @param elements a list of elements to search |
| * @param name the name to find |
| * @param trySubNames whether to attempt a loose name match, if necessary |
| * |
| * @return the first named element found, or <code>null</code> if none |
| */ |
| private static ENamedElement matchName(ENamedElement[] elements, |
| String name, boolean trySubNames) { |
| //match on exact name |
| for (int i = 0; i < elements.length; ++i) { |
| if (elements[i].getName().equalsIgnoreCase(name)) { |
| return elements[i]; |
| } |
| } |
| |
| if (trySubNames) { |
| //match on subname |
| for (int i = 0; i < elements.length; ++i) { |
| if ((elements[i].getName().indexOf(name) != NONE) |
| || (name.indexOf(elements[i].getName()) != NONE)) { |
| return elements[i]; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Queries whether the specified <code>eObject</code> is contained within |
| * another. This differs from the |
| * {@link org.eclipse.emf.ecore.util.EcoreUtil#isAncestor(org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject)} |
| * method in checking for equality rather than identity. |
| * |
| * @param eParent an element |
| * @param eObject an element to see whether it is contained in the |
| * <code>eParent</code> |
| * @return <code>true</code> if the <code>eObject</code> is contained within |
| * an element equal to the <code>eParent</code>; |
| * <code>false</code>, otherwise |
| */ |
| public static boolean isChild(EObject eParent, EObject eObject) { |
| EObject eContainer = eObject.eContainer(); |
| while (eContainer != null) { |
| if (eContainer.equals(eParent)) { |
| return true; |
| } |
| eContainer = eContainer.eContainer(); |
| } |
| return false; |
| } |
| |
| /** |
| * Queries whether two elements are both |
| * {@linkplain IClipboardSupport#isNameable(EObject) nameable} and have |
| * the same name (ignoring case). |
| * |
| * @param eObject1 an object |
| * @param eObject2 another object |
| * @return <code>true</code> if both objects are nameable and have the |
| * same name (ignoring case); <code>false</code>, otherwise |
| * |
| * @see IClipboardSupport#isNameable(EObject) |
| */ |
| public static boolean hasNameCollision(EObject eObject1, EObject eObject2) { |
| if (eObject1.eClass().equals(eObject2.eClass())) { |
| IClipboardSupport cs = createClipboardSupport(eObject1); |
| |
| //if the same object then no collision |
| if (eObject1.equals(eObject2)) { |
| return false; |
| } else if (isNameable(eObject1)) { |
| String childEObjectName = cs.getName(eObject2); |
| if (!isEmptyName(childEObjectName)) { |
| if (childEObjectName.equalsIgnoreCase(cs.getName(eObject1))) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Queries whether the specified element name is empty, indicating that the |
| * element does not have a name. This is the case if the name is either the |
| * <code>null</code> reference or an empty string. No consideration is |
| * made for names that may be different if they were trimmed (for example, a |
| * name consisting of just a bunch of blanks is not "empty"). |
| * |
| * @param elementName |
| * an element (eobject) name |
| * @return <code>true</code> if the <code>elementName</code> is either |
| * <code>null</code> or empty; <code>false</code>, otherwise |
| */ |
| private static boolean isEmptyName(String elementName) { |
| return (elementName == null) || (elementName.length() == 0); |
| } |
| |
| /** |
| * Determines whether an object is nameable. |
| * |
| * @param eObject an object |
| * |
| * @return whether it is nameable |
| */ |
| private static boolean isNameable(EObject eObject) { |
| return createClipboardSupport(eObject).isNameable(eObject); |
| } |
| |
| /** |
| * Queries whether an object is |
| * {@linkplain IClipboardSupport#isNameable(EObject) nameable} and have |
| * the same name (ignoring case) as any element in a <code>list</code>. |
| * |
| * @param list a ist of objects |
| * @param eObject another object |
| * @return <code>true</code> if the <code>eObject</code>is nameable and |
| * has the same name (ignoring case) as any object in the |
| * <code>list</code>; <code>false</code>, otherwise |
| * |
| * @see IClipboardSupport#isNameable(EObject) |
| */ |
| public static boolean hasNameCollision(Collection list, EObject eObject) { |
| Iterator it = list.iterator(); |
| EObject eOther = null; |
| while (it.hasNext()) { |
| eOther = (EObject) it.next(); |
| if (hasNameCollision(eOther, eObject)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Given an object that has a name collision with one or more elements in |
| * a <code>list</code>, renames it to have a unique name. |
| * |
| * @param list a list of objects |
| * @param eObject another object whose name collides with the <code>list</code> |
| * @param prefix the prefix to append; it will be modified by '_1', '_2', |
| * etc. as necessary |
| * |
| * @see #hasNameCollision(Collection, EObject) |
| */ |
| public static void rename(Collection list, EObject eObject, String prefix) { |
| if (isNameable(eObject) == false) { |
| return; |
| } |
| String new_name = null; |
| String name = createClipboardSupport(eObject).getName(eObject); |
| String prefix_underscore = prefix + UNDERSCORE; |
| if (name.startsWith(prefix_underscore)) { |
| int close_index = name.indexOf(UNDERSCORE, prefix_underscore |
| .length()); |
| if (close_index != NONE) { |
| String copy_prefix = name.substring(0, close_index); |
| int open_index = copy_prefix.indexOf(UNDERSCORE); |
| String copy_count_str = copy_prefix.substring(open_index + 1); |
| try { |
| int copy_count = Integer.parseInt(copy_count_str); |
| String name_proper = name.substring(close_index + 1).trim(); |
| new_name = prefix_underscore + ++copy_count + UNDERSCORE |
| + name_proper; |
| } catch (NumberFormatException nfe) { |
| ClipboardPlugin.catching(ClipboardSupportUtil.class, |
| "rename", nfe); //$NON-NLS-1$ |
| new_name = null; |
| } |
| } |
| } |
| |
| if (new_name == null) { |
| new_name = prefix_underscore + ONE + UNDERSCORE + name; |
| } |
| |
| createClipboardSupport(eObject).setName(eObject, new_name); |
| |
| //check this new name itself does not collide with an existing one |
| if (hasNameCollision(list, eObject)) { |
| rename(list, eObject, prefix); |
| } |
| } |
| |
| /** |
| * Queries whether two collections intersect. |
| * |
| * @param collection1 a collection |
| * @param collection2 another |
| * |
| * @return <code>true</code> if they have any elements in common; |
| * <code>false</code>, otherwise |
| */ |
| public static boolean containsAny(Collection collection1, |
| Collection collection2) { |
| Iterator it = collection2.iterator(); |
| while (it.hasNext()) { |
| if (collection1.contains(it.next())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Resolves a <code>proxy</code>, using the specified ID map. If a proxy |
| * resolves to another proxy, then this procedure repeats until either a |
| * non-proxy is found or it is not resolved. |
| * |
| * @param proxy a proxy |
| * @param idToEObjectMap a mapping of element ID strings to {@link EObject}s |
| * @return a resolved element, or a proxy if it could not be resolved. In |
| * either case, the result may be different than the original |
| * <code>proxy</code> (this is different from the behaviour of the |
| * {@link org.eclipse.emf.ecore.util.EcoreUtil#resolve(org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject)} |
| * method |
| */ |
| public static EObject resolve(EObject proxy, Map idToEObjectMap) { |
| URI proxyUri = ((InternalEObject) proxy).eProxyURI(); |
| if (proxyUri != null) { |
| try { |
| String id = getProxyID(proxyUri); |
| if ((id == null) || (id.length() == 0)) { |
| id = proxyUri.fragment(); |
| } |
| EObject resolvedObject = (EObject) idToEObjectMap.get(id); |
| if (resolvedObject != null && resolvedObject != proxy) { |
| return resolve(resolvedObject, idToEObjectMap); |
| } |
| } catch (Exception exception) { |
| ClipboardPlugin.catching(ClipboardSupportUtil.class, RESOLVE, exception); |
| } |
| } |
| return proxy; |
| } |
| |
| /** |
| * Resolves a <code>proxy</code>, using the specified resource. If a proxy |
| * resolves to another proxy, then this procedure repeats until either a |
| * non-proxy is found or it is not resolved. |
| * |
| * @param proxy a proxy |
| * @param resource the resource containing a mapping of element ID strings to {@link EObject}s |
| * @return a resolved element, or a proxy if it could not be resolved. In |
| * either case, the result may be different than the original |
| * <code>proxy</code> (this is different from the behaviour of the |
| * {@link org.eclipse.emf.ecore.util.EcoreUtil#resolve(org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject)} |
| * method |
| */ |
| public static EObject resolve(EObject proxy, XMLResource resource) { |
| URI proxyUri = ((InternalEObject) proxy).eProxyURI(); |
| if (proxyUri != null) { |
| try { |
| String id = getProxyID(proxyUri); |
| if ((id == null) || (id.length() == 0)) { |
| id = proxyUri.fragment(); |
| } |
| EObject resolvedObject = resource.getEObject(id); |
| if (resolvedObject != null && resolvedObject != proxy) { |
| return resolve(resolvedObject, resource); |
| } |
| } catch (Exception exception) { |
| ClipboardPlugin.catching(ClipboardSupportUtil.class, RESOLVE, exception); |
| } |
| } |
| return proxy; |
| } |
| |
| /** |
| * Obtains the proxy ID from a proxy, accounting for possible fragment |
| * queries. |
| * |
| * @param proxyUri a proxy URI |
| * @return the fragment portion, minus the query (if any) |
| */ |
| private static String getProxyID(URI proxyUri) { |
| |
| String uriFragment = proxyUri.fragment(); |
| |
| int index = uriFragment.indexOf('?'); |
| |
| return index != -1 ? uriFragment.substring(0, index) |
| : uriFragment; |
| } |
| |
| /** |
| * Obtains the containment feature of a parent element into which a child |
| * should be pasted, that best matches the child's original containment |
| * feature. |
| * |
| * @param parentEObject the target element into which a child is to be |
| * pasted |
| * @param eObject the child element to be pasted into the parent |
| * @param originalReference the child's original containment reference |
| * |
| * @return the best-match containment reference, or <code>null</code> if |
| * child <code>eObject</code> simply cannot be contained by the |
| * <code>parentEObject</code> |
| */ |
| public static EReference getPasteContainmentFeature(EObject parentEObject, |
| EObject eObject, EReference originalReference) { |
| //NOTE: originalReference could be null |
| List compatibleRefrencesList = new ArrayList(); |
| EClass parentEClass = parentEObject.eClass(); |
| EClass childEClass = eObject.eClass(); |
| Iterator it = parentEClass.getEAllReferences().iterator(); |
| EReference parentReference = null; |
| while (it.hasNext()) { |
| parentReference = (EReference) it.next(); |
| // check if the parentReference is a containment reference |
| if (parentReference.isContainment()) { |
| if ((originalReference != null) |
| && parentReference.equals(originalReference)) { |
| //perfect match...return it |
| return parentReference; |
| } |
| if (isMutable(parentReference)) { |
| if (createClipboardSupport(parentEObject).canContain( |
| parentEObject, parentReference, childEClass)) { |
| //holds same eObject type, collect it |
| compatibleRefrencesList.add(parentReference); |
| } |
| } |
| } |
| } |
| |
| //found none |
| if (compatibleRefrencesList.size() == 0) { |
| return null; |
| } else if (compatibleRefrencesList.size() == 1) { |
| //only one, then return it |
| return (EReference) compatibleRefrencesList.get(0); |
| } |
| |
| //didn't return from above, ok try to choose one based on name match. |
| EReference[] references = (EReference[]) compatibleRefrencesList |
| .toArray(EMPTY_REF_ARRAY); |
| //match on exact name of originalReference. |
| EReference ref = null; |
| if (originalReference != null) { |
| ref = (EReference) ClipboardSupportUtil.matchName(references, |
| originalReference.getName(), true); |
| } |
| |
| return (ref == null) ? (EReference) ClipboardSupportUtil.matchName( |
| references, childEClass.getName(), true) |
| : ref; |
| } |
| } |