/****************************************************************************** * 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.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.emf.ecore.EAnnotation; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.FeatureMapUtil; import org.eclipse.emf.ecore.xmi.XMLResource; import org.eclipse.gmf.runtime.emf.clipboard.core.internal.MergedEObjectInfo; import org.eclipse.gmf.runtime.emf.clipboard.core.internal.ObjectCopyType; import org.eclipse.gmf.runtime.emf.clipboard.core.internal.PasteIntoParentOperation; import org.eclipse.gmf.runtime.emf.clipboard.core.internal.ResourceInfoProcessor; import org.eclipse.gmf.runtime.emf.clipboard.core.internal.l10n.EMFClipboardCoreMessages; /** * An operation to paste a child object into a new target parent object. * * @author Yasser Lulu */ public class PasteChildOperation extends BasePasteOperation { /** * Localized prefix to prepend onto element names to disambiguate them * in resolving paste collisions. */ protected static final String COPY = EMFClipboardCoreMessages.pasteChildOperation_copyPrefix; /** * A reusable empty array of paste child operations. */ protected static final PasteChildOperation[] EMPTY_ARRAY = new PasteChildOperation[0]; private Map auxiliaryChildPasteProcessMap; private PasteChildOperation mainChildPasteProcess; private PasteChildOperation postPasteOperation; private List alwaysCopyObjectPasteOperations; private ObjectInfo childObjectInfo; private EObject childEObject; private EObject pastedElement; private EObject copyParentEObject; private ObjectInfo copyParentObjectInfo; private EObject directContainerEObject; private OverridePasteChildOperation overrideChildPasteOperation; private EReference containmentFeature; private Map embeddedCopyParentObjectInfoMap = new HashMap(); /** * Initializes me. * * @param parentPasteProcess my superordinate operation, that created me * in order to paste one of its copied objects into its target parent * object * @param childEObjectInfo the metadata for the child object to be pasted */ public PasteChildOperation(PasteIntoParentOperation parentPasteProcess, ObjectInfo childEObjectInfo) { super(parentPasteProcess); this.childObjectInfo = childEObjectInfo; auxiliaryChildPasteProcessMap = new HashMap(); } /** * Gets the containment feature into which I would prefer to paste my child * element into the parent. * * @return the preferred containment feature to paste into */ protected EReference getContainmentFeature() { if (containmentFeature == null) { containmentFeature = getParentPasteProcess().getContainmentFeature( getEObject()); } return containmentFeature; } /** * Initializes me as an auxiliary to another child paste operation. * * @param mainChildPasteProcess the main child paste operation * @param childEObjectInfo the metadata for the child object to be pasted */ private PasteChildOperation(PasteChildOperation mainChildPasteProcess, ObjectInfo childEObjectInfo) { this(mainChildPasteProcess.getParentPasteProcess(), childEObjectInfo); this.mainChildPasteProcess = mainChildPasteProcess; } /** * Initializes me as an override- or post-paste operation. * * @param mainChildPasteProcess the paste-child process that I am overriding * or extending */ protected PasteChildOperation(PasteChildOperation mainChildPasteProcess) { this(mainChildPasteProcess.getParentPasteProcess(), mainChildPasteProcess.childObjectInfo); } /** * Obtains the direct container object, from the deserialized copied elements, * of the object that I am pasting. * * @return my object's direct container */ protected EObject getLoadedDirectContainerEObject() { if (directContainerEObject == null) { directContainerEObject = getLoadedEObject(getChildObjectInfo().containerId); } return directContainerEObject; } /** * Obtains the paste operation that created me to paste a child into the * target parent element. * * @return my parent operation */ public PasteIntoParentOperation getParentPasteProcess() { return (PasteIntoParentOperation) getSpawningPasteOperation(); } /** * Obtains the paste operation that pastes the auxiliary object indicated * by the specified object info. * * @param auxiliaryChildEObjectInfo the auxiliary object's info * @return the paste operation that pastes the auxiliary object */ protected PasteChildOperation getAuxiliaryChildPasteProcess( ObjectInfo auxiliaryChildEObjectInfo) { PasteChildOperation auxiliaryChildPasteProcess = (PasteChildOperation) auxiliaryChildPasteProcessMap .get(auxiliaryChildEObjectInfo); if (auxiliaryChildPasteProcess == null) { auxiliaryChildPasteProcess = makeAuxiliaryChildPasteProcess(auxiliaryChildEObjectInfo); if (auxiliaryChildPasteProcess != null) { auxiliaryChildPasteProcessMap.put(auxiliaryChildEObjectInfo, auxiliaryChildPasteProcess); } } return auxiliaryChildPasteProcess; } /** * Constructs a paste operation to paste the auxiliary object indicated by * its object info. * * @param auxiliaryChildEObjectInfo the auxiliary object's info * @return the appropriate paste child operation */ protected PasteChildOperation makeAuxiliaryChildPasteProcess( ObjectInfo auxiliaryChildEObjectInfo) { return new PasteChildOperation(this, auxiliaryChildEObjectInfo); } /** * Retrieves the object that I am pasting from the deserialized clipboard * string into the target parent object. * * @return the object that I am pasting */ public EObject getEObject() { if (childEObject == null) { childEObject = getLoadedEObject(getChildObjectInfo().objId); } return childEObject; } /** * After pasting is completed, obtains the element that I pasted. * * @return the pasted element, or <code>null</code> if I did not succeed * in pasting */ public EObject getPastedElement() { return pastedElement; } /** * Looks up an object deserialized from the clipboard string by its ID. * * @param objId the object ID to look up * @return the corresponding object, or <code>null</code> if not found */ protected EObject getLoadedEObject(String objId) { return getParentPasteProcess().getLoadedEObject(objId); } /** * Looks up the ID of an object deserialized from the clipboard string. * * @param eObject the object whose ID we want to look up * @return the corresponding ID, or <code>null</code> if not found */ protected String getLoadedEObjectID(EObject eObject) { return getParentPasteProcess().getLoadedEObjectID(eObject); } /** * Queries whether the object that I am pasting had its parent object * copied along with it, as an alternative element to try to paste when I * can't be directly pasted into the target element. * * @return whether my copy object was copied with its parent */ protected boolean hasCopyParent() { return (ResourceInfoProcessor.NONE .equals(getChildObjectInfo().copyParentId) == false); } /** * Queries whether the object that I am pasting was copied because it * is always copied along with an element that was originally selected for * copying. * * @return whether my copy object is copied because of a "copy always" rule */ protected boolean isCopyAlways() { return (getChildObjectInfo().objCopyType .equals(ObjectCopyType.OBJ_COPY_TYPE_ALWAYS)); } /** * Gets the original parent of the object that I am pasting, if it was * copied along with the child. * * @return the copy parent, or <code>null</code> if it was not copied * * @see #hasCopyParent() */ protected EObject getCopyParentEObject() { if (copyParentEObject == null) { copyParentEObject = getLoadedEObject(getChildObjectInfo().copyParentId); } return copyParentEObject; } /** * Looks up an object in the resource to which we are pasting, by its ID. * * @param objId the object ID to look up * @return the corresponding object, or <code>null</code> if not found */ protected EObject getEObject(String objId) { return getParentPasteProcess().getEObject(objId); } /** * Looks up the ID of an object in the resource to which we are pasting. * * @param eObject the object to look up its ID * @return the corresponding ID, or <code>null</code> if not found */ protected String getEObjectID(EObject eObject) { return getParentPasteProcess().getEObjectID(eObject); } /** * Finds some parent in the containment chain (ancestry) of the parent * element into which I am pasting that my child element can be pasted into. * * @param preferredTypeName the fully-qualified class name of the preferred * element type to paste into * @return the suitable parent, if found, otherwise <code>null</code> */ protected PasteTarget getSuitableParentUsingAncestry(String preferredTypeName) { PasteTarget suitableParent = getSuitableParentUsingAncestry( getParentTarget(), preferredTypeName, true); if (suitableParent == null) { suitableParent = getSuitableParentUsingAncestry(getParentTarget(), preferredTypeName, false); } return suitableParent; } private PasteTarget getSuitableParentUsingAncestry( PasteTarget potentialParent, String preferredTypeName, boolean strictMatch) { PasteTarget suitableParent = checkPotentialParent(potentialParent, preferredTypeName, strictMatch); if (suitableParent == null && !potentialParent.isResource()) { EObject potentialParentObject = (EObject)potentialParent.getObject(); while ((suitableParent == null) && (potentialParentObject.eContainer() != null)) { potentialParentObject = potentialParentObject.eContainer(); suitableParent = checkPotentialParent(new PasteTarget(potentialParentObject), preferredTypeName, strictMatch); } } return suitableParent; } /** * After pasting has completed, obtains the pasted object corresponding * to the specified originally copied object. * * @param eObject the originally copied object * @return the corresponding pasted object, or <code>null</code> if the * <code>eObject</code> was not successfully pasted */ protected EObject getPastedEObject(EObject eObject) { String id = getEObjectID(eObject); if (id != null) { //it has been pasted as is return eObject; } else { //maybe it has been merged? MergedEObjectInfo info = (MergedEObjectInfo) getAllMergedElementsMap() .get(eObject); if (info != null) { if (info.targetEObjects.size() == 1) { return (EObject) info.targetEObjects.get(0); } else if (info.targetEObjects.size() > 1) { //got merged more than once, pick most suitable! Iterator it = info.targetEObjects.iterator(); while (it.hasNext()) { EObject mergeTarget = (EObject) it.next(); if ((getParentTarget().isResource() && getParentTarget().getObject() == mergeTarget.eResource()) || ClipboardSupportUtil.isChild(getParentEObject(), mergeTarget)) { return mergeTarget; } } //no suitable one, then pick the first return (EObject) info.targetEObjects.get(0); } } } return null; } /** * After pasting has completed, obtains the pasted object corresponding * to the specified originally copied object's parent. * * @return the corresponding pasted object, or <code>null</code> if the * copy parent was not successfully pasted */ protected EObject getPastedDirectCopyParent() { return getPastedEObject(getLoadedDirectContainerEObject()); } private EObject doPasteIntoNearestCopyParent( EObject topMostCopyParentEObject) throws Exception { EObject nearestParent = getLoadedEObject(getChildObjectInfo().containerId); while (nearestParent.equals(topMostCopyParentEObject) == false) { EObject parentElement = doPasteIntoCopyParent(makeEmbeddedCopyParentObjectInfo(nearestParent)); if (parentElement != null) { return parentElement; } nearestParent = nearestParent.eContainer(); } return null; } private EObject doPasteIntoCopyParent(ObjectInfo theCopyParentObjectInfo) throws Exception { PasteChildOperation copyParentProcess = getAuxiliaryChildPasteProcess(theCopyParentObjectInfo); copyParentProcess.paste(); EObject pastedCopyParent = copyParentProcess.getPastedElement(); if (pastedCopyParent != null) { //the direct copy parent should have been pasted correctly by now return doPasteInto(getPastedDirectCopyParent()); } return null; } private ObjectInfo makeEmbeddedCopyParentObjectInfo( EObject embeddedCopyParent) { ObjectInfo objectInfo = (ObjectInfo) embeddedCopyParentObjectInfoMap .get(embeddedCopyParent); if (objectInfo == null) { objectInfo = new ObjectInfo(); objectInfo.objCopyType = ObjectCopyType.OBJ_COPY_TYPE_PARENT; objectInfo.objId = getLoadedEObjectID(embeddedCopyParent); objectInfo.containerId = getLoadedEObjectID(embeddedCopyParent .eContainer()); objectInfo.containerClass = embeddedCopyParent.eContainer() .eClass().getInstanceClassName(); if (objectInfo.objId.equals(getChildObjectInfo().copyParentId) == false) { objectInfo.copyParentId = getChildObjectInfo().copyParentId; } else { objectInfo.copyParentId = ResourceInfoProcessor.NONE; } objectInfo.hints = ResourceInfoProcessor.NONE; //cache it embeddedCopyParentObjectInfoMap.put(embeddedCopyParent, objectInfo); } return objectInfo; } private EObject doPasteIntoCopyParent() throws Exception { //check if copyParentEObject exists in the target model already //try matching direct copy parent ID. EObject existingCopyParentEObject = getEObject(getChildObjectInfo().containerId); if (existingCopyParentEObject != null) { return doPasteInto(existingCopyParentEObject); } //check if the copy-parent has been //pasted already by a sibling paste operation that executed before us? EObject pastedDirectCopyParent = getPastedDirectCopyParent(); if (pastedDirectCopyParent != null) { //the direct copy parent should have been pasted correctly already return doPasteInto(pastedDirectCopyParent); } EObject nearestParent = null; if (isCopyParentDirectParent() == false) { nearestParent = getLoadedEObject(getChildObjectInfo().containerId); EObject perent = nearestParent.eContainer(); EObject root = getCopyParentEObject(); while ((perent != null) && (perent.equals(root) == false)) { existingCopyParentEObject = getPastedEObject(perent); if (existingCopyParentEObject != null) { break; } nearestParent = perent; perent = nearestParent.eContainer(); } if (existingCopyParentEObject == null) { //check the root itself existingCopyParentEObject = getPastedEObject(root); } } if (existingCopyParentEObject != null) { //the nearestParent copy parent should have been pasted correctly // already //paste the nearest-parent itself first, the paste the child into // it afterwards return doPasteIntoCopyParent(makeEmbeddedCopyParentObjectInfo(nearestParent)); } else { //no parent with same ID, and the copy-parent not pasted already, //then try other ways to match a parent PasteTarget possibleParent = getSuitableParentUsingAncestry(getLoadedDirectContainerEObject() .eClass().getInstanceClassName()); if (possibleParent != null) { return doPasteInto(possibleParent); } else { //no suitable exisiting parent then the copy-parent itself //needs to be pasted first EObject element = doPasteIntoNearestCopyParent(getCopyParentEObject()); if (element != null) { //found a nearest copy parent and pasted it successfully return element; } //now final try: use the root copy Parent? return doPasteIntoCopyParent(getCopyParentObjectInfo()); } } } /** * Gets the target object into which we are pasting a child. * * @return the parent (target) object of the paste operation */ public EObject getParentEObject() { return getParentPasteProcess().getEObject(); } /** * Gets the target object into which we are pasting a child. * * @return the parent (target) object of the paste operation */ public PasteTarget getParentTarget() { return getParentPasteProcess().getPasteTarget(); } /** * Obtains the XML resource into which we are pasting. * * @return the target resource */ public XMLResource getParentResource() { return getParentPasteProcess().getParentResource(); } /** * Gets an operation to be invoked after I have completed my pasting, to do * some follow-up pasting. {@link OverridePasteChildOperation}s can * redefine this method to provide custom post-paste behaviour. * <p> * <b>IMPORTANT:</b> This function must be invoked AFTER the paste() * function has been called. * </p> * * @return an additional operation to do more pasting */ public PasteChildOperation getPostPasteOperation() { return (overrideChildPasteOperation != null) ? overrideChildPasteOperation .getPostPasteOperation() : doGetPostPasteOperation(); } private PasteChildOperation doGetPostPasteOperation() { if ((postPasteOperation == null) && (getPastedElement() != null) && (getAlwaysCopyObjectPasteOperations().isEmpty() == false)) { postPasteOperation = new PostPasteChildOperation(this, getAlwaysCopyObjectPasteOperations()); } return postPasteOperation; } public void paste() throws Exception { //check if this object has been pasted/merged already for some reason: //e.g, copied once because it is a copy-parent, and now we are asked //to copy it because it is a copyAlways if (getPastedEObject(getEObject()) != null) { setPastedElement(getEObject()); addPastedElement(getPastedElement()); //set post paste to a NULL operation postPasteOperation = PostPasteChildOperation .makeNullPostPasteChildOperation(this); return; } //handle diagrams if (getClipboardOperationHelper().shouldOverrideChildPasteOperation( getParentEObject(), getEObject())) { overrideChildPasteOperation = getClipboardOperationHelper() .getOverrideChildPasteOperation(this); //if the parent was not suitable for pasting a diagram, this // happens if (overrideChildPasteOperation != null) { overrideChildPasteOperation.paste(); setPastedElement(overrideChildPasteOperation.getPastedElement()); } else { addPasteFailuresObject(getEObject()); } return; } if (hasCopyParent()) { setPastedElement(doPasteIntoCopyParent()); } else { EObject element = null; //either it is not a copyAlways, or it is a copyAlways // whose //original parent didn't resolve, thus, proceed normally //by trying to paste in target obj element = doPasteInto(getParentTarget()); if (element == null) { /*------------- //failed to copy in target parent...then check if it is a copy-always and its // original parent resolves in target model if (isCopyAlways()) { EObject resolvedCopyAlwaysParent = getEObject(getChildObjectInfo().containerId); if (resolvedCopyAlwaysParent != null) { //found original parent for this copyAlways object, // then use it, //instead of user selected parent element = doPasteInto(resolvedCopyAlwaysParent); } } -------------*/ if ((element == null) && ((getChildObjectInfo() .hasHint(ClipboardUtil.PASTE_TO_TARGET_PARENT)) || (isCopyAlways()))) { PasteTarget possibleParent = getSuitableParentUsingAncestry(getChildObjectInfo().containerClass); if (possibleParent != null) { element = doPasteInto(possibleParent); } } } setPastedElement(element); } //did we succeed? if (getPastedElement() != null) { addPastedElement(getPastedElement()); } else { addPasteFailuresObject(getEObject()); } } /** * Pastes my child object into the specified object. * * @param pasteIntoEObject the object to paste into * @return the newly pasted object, or <code>null</code> if the paste did * not succeed */ protected EObject doPasteInto(EObject pasteIntoEObject) { if (pasteIntoEObject != null) { EReference reference = getPasteContainmentFeature(pasteIntoEObject); if (reference != null) { return doPasteInto(pasteIntoEObject, reference); } } return null; } /** * Pastes my child object into the specified object. * * @param pasteTarget the object to paste into * @return the newly pasted object, or <code>null</code> if the paste did * not succeed */ protected EObject doPasteInto(PasteTarget pasteTarget) { if (pasteTarget != null) { if (pasteTarget.isResource()) { return doPasteInto((Resource)pasteTarget.getObject()); } else { return doPasteInto((EObject)pasteTarget.getObject()); } } return null; } /** * Pastes my child object into the specified containment reference of an * object. * * @param pasteIntoEObject the object to paste into * @param reference the containment reference to paste into * @return the newly pasted object, or <code>null</code> if the paste did * not succeed */ protected EObject doPasteInto(EObject pasteIntoEObject, EReference reference) { EObject childElement = null; Object value = pasteIntoEObject.eGet(reference, true); if (FeatureMapUtil.isMany(pasteIntoEObject,reference)) { if (handleCollision(reference, (List) value, getEObject(), getChildObjectInfo())) { childElement = ClipboardSupportUtil.appendEObjectAt( pasteIntoEObject, reference, getEObject()); } else if (getPastedElement() != null) { // our pasted element was already assigned by a merge action childElement = getPastedElement(); } } else { // reference is single if (handleCollision(reference, (EObject) value, getEObject(), getChildObjectInfo())) { childElement = ClipboardSupportUtil.setEObject( pasteIntoEObject, reference, getEObject()); } else if (getPastedElement() != null) { // our pasted element was already assigned by a merge action childElement = getPastedElement(); } } return childElement; } /** * Pastes my child object into the specified resource * * @param pasteIntoResource the resource to paste into * @return the newly pasted object, or <code>null</code> if the paste did * not succeed */ protected EObject doPasteInto(Resource pasteIntoResource) { EObject childElement = null; if (handleCollision(null, pasteIntoResource.getContents(), getEObject(), getChildObjectInfo())) { childElement = ClipboardSupportUtil.appendEObject( pasteIntoResource, getEObject()); } else if (getPastedElement() != null) { // our pasted element was already assigned by a merge action childElement = getPastedElement(); } return childElement; } /** * Obtains a list of operations to paste the "copy always" objects related * to the object that I am pasting. By default, these will be executed * as a post-paste operation following me. * * @return the copy-always object paste operations (which may be empty) * * @see #getPostPasteOperation() */ protected List getAlwaysCopyObjectPasteOperations() { if (alwaysCopyObjectPasteOperations == null) { alwaysCopyObjectPasteOperations = new ArrayList(); Iterator alwaysCopyEObjectInfoIt = getChildObjectInfo() .getCopyAlwaysObjectInfoList().iterator(); while (alwaysCopyEObjectInfoIt.hasNext()) { getProgressMonitor().worked(WORK_UNIT); if (isCancelled()) { throwCancelException(); } ObjectInfo alwaysCopyObjectInfo = (ObjectInfo) alwaysCopyEObjectInfoIt .next(); if (shouldPasteAlwaysCopyObject(alwaysCopyObjectInfo)) { PasteChildOperation pasteOperation = getAuxiliaryChildPasteProcess(alwaysCopyObjectInfo); if (pasteOperation != null) { alwaysCopyObjectPasteOperations.add(pasteOperation); } } } //while always-copy } return alwaysCopyObjectPasteOperations; } /** * Queries whether we should paste the specified "copy always" object * associated with the element we are pasting. This accounts for the * possibility that the "copy always" object might collide with some element * already in the target resource. * * @param alwaysCopyObjectInfo info indicating the "copy always" object * @return <code>true</code> if the "copy always" object should be pasted; * <code>false</code>, otherwise */ protected boolean shouldPasteAlwaysCopyObject( ObjectInfo alwaysCopyObjectInfo) { //for a copy always object that exists in the model already, check if // this //kind of object is "critical", then we need to paste it, note that we // are using //the exisiting object to query for PasteOption since what matter is // the type //and not the particular instance itself EObject existingObject = getEObject(alwaysCopyObjectInfo.objId); EObject loadedEObject = getLoadedEObject(alwaysCopyObjectInfo.objId); return ((existingObject == null) || (getClipboardOperationHelper() .getPasteCollisionAction(loadedEObject.eClass()) == PasteAction.ADD)); } private PasteTarget checkPotentialParent(PasteTarget potentialParent, String preferredTypeName, boolean strictMatch) { //match parent on type, if not then try ability to contain the child if ((potentialParent.getObject() instanceof EAnnotation) == false) { if (potentialParent.isResource()) { if (preferredTypeName == null || strictMatch == false) { return potentialParent; } } else { EObject potentialParentObject = (EObject)potentialParent.getObject(); if (potentialParentObject.eClass().getInstanceClassName().equals( preferredTypeName)) { return potentialParent; } else if ((strictMatch == false) && (getPasteContainmentFeature(potentialParentObject) != null)) { return potentialParent; } } } return null; } /** * Consults the clipboard support to get the best-match containment feature * of the specified parent object to paste my child object into, according * to the copied object's original containment feature. * * @param parentEObject the parent to paste into * @return the most appropriate containment feature for the child, or * <code>null</code> if the parent cannot contain the child at all */ public EReference getPasteContainmentFeature(EObject parentEObject) { return ClipboardSupportUtil.getPasteContainmentFeature(parentEObject, getEObject(), getContainmentFeature()); } /** * Handles the collision of an <code>eObject</code> that is to be pasted * into the specified values of a reference, where it collides with one or * more of these existing elements. This may involve, among other things, * renaming the object to be pasted in order to make its name unique. * * @param reference the many containment reference into which we want to * paste the <code>eObject</code> * @param list the current value (list of {@link EObject}s) of the * containment reference * @param eObject the object that we want to paste, which collides with one * or more elements in the <code>list</code> * @param eObjectInfo the <code>eObject</code>'s object info meta-data * * @return <code>true</code> if the collision was resolved so that we * can go ahead and paste the <code>eObject</code> as intended; * false, otherwise */ protected boolean handleCollision(EReference reference, List list, EObject eObject, ObjectInfo eObjectInfo) { PasteAction pasteCollisionAction = (eObjectInfo.objCopyType .equals(ObjectCopyType.OBJ_COPY_TYPE_ALWAYS)) ? PasteAction.CLONE : getClipboardOperationHelper().getPasteCollisionAction( eObject.eClass()); if (pasteCollisionAction == PasteAction.DISCARD) { //Do not paste. Such elements are typically copied in order to find // an appropriate parent return false; } if (list.isEmpty()) { return true; } EObject object = null; Iterator it = list.iterator(); while (it.hasNext()) { object = (EObject) it.next(); if (ClipboardSupportUtil.hasNameCollision(object, eObject)) { if (pasteCollisionAction.equals(PasteAction.ADD)) { //Create new element with different name ClipboardSupportUtil.rename(list, eObject, COPY); return true; //insert child } else if (pasteCollisionAction.equals(PasteAction.REPLACE)) { if (canBeReplaced(object)) { //Remove collision element, if any. Create new element // in the same location. if (reference == null) { // paste target is the resouce ClipboardSupportUtil.destroyEObjectInResource(object); } else { ClipboardSupportUtil.destroyEObjectInCollection(object .eContainer(), reference, object); } return true; } return false; //ignore it since we can't replace the other } else if (pasteCollisionAction.equals(PasteAction.IGNORE)) { //Leave existing element, if found. Otherwise create new // element. return false; } else if (pasteCollisionAction.equals(PasteAction.MERGE)) { mergeEObjects(eObjectInfo .hasHint(ClipboardUtil.MERGE_HINT_WEAK), object, eObject, eObjectInfo); // record the existing object that we collided with as the // pasted element, so that we will know that the logical // paste operation succeeded setPastedElement(object); return false; //don't insert child since we merged it } else if (pasteCollisionAction.equals(PasteAction.CLONE)) { //Always copy, even if indirectly selected. //Y.L. treat it as add??? ClipboardSupportUtil.rename(list, eObject, COPY); return true; //insert child } } //hasNameCollision } //while return true; //insert child } /** * Handles the collision of an <code>eObject</code> that is to be pasted * into the specified scalar reference, where it collides with the * existing element. * * @param reference the scalar containment reference into which we want to * paste the <code>eObject</code> * @param object the current value of the containment reference * @param eObject the object that we want to paste, which collides with one * or more elements in the <code>list</code> * @param eObjectInfo the <code>eObject</code>'s object info meta-data * * @return <code>true</code> if the collision was resolved so that we * can go ahead and paste the <code>eObject</code> as intended; * false, otherwise */ protected boolean handleCollision(EReference reference, EObject object, EObject eObject, ObjectInfo eObjectInfo) { PasteAction pasteCollisionAction = (eObjectInfo.objCopyType .equals(ObjectCopyType.OBJ_COPY_TYPE_ALWAYS)) ? PasteAction.CLONE : getClipboardOperationHelper().getPasteCollisionAction( eObject.eClass()); if (pasteCollisionAction.equals(PasteAction.DISCARD)) { //Do not paste. Such elements are typically copied in order to find // an appropriate parent return false; } if (object == null) { return true; } if (pasteCollisionAction.equals(PasteAction.ADD)) { if (canBeReplaced(object)) { //Create new element with different name //treat as Replace, since it is a single ref.??? ClipboardSupportUtil.destroyEObject(object.eContainer(), reference); return true; } return false; } else if (pasteCollisionAction.equals(PasteAction.REPLACE)) { if (canBeReplaced(object)) { //Remove collision element, if any. Create new element in the // same location. ClipboardSupportUtil.destroyEObject(object.eContainer(), reference); return true; } return false; } else if (pasteCollisionAction.equals(PasteAction.IGNORE)) { //Leave existing element, if found. Otherwise create new element. return false; } else if (pasteCollisionAction.equals(PasteAction.MERGE)) { mergeEObjects(eObjectInfo.hasHint(ClipboardUtil.MERGE_HINT_WEAK), object, eObject, eObjectInfo); // record the existing object that we collided with as the // pasted element, so that we will know that the logical // paste operation succeeded setPastedElement(object); return false; //don't insert child since we merged it } else if (pasteCollisionAction.equals(PasteAction.CLONE)) { if (canBeReplaced(object)) { //Always copy, even if indirectly selected. //treat as Replace??? ClipboardSupportUtil.destroyEObject(object.eContainer(), reference); return true; } return false; } return true; //insert child } /** * Merges the elements to be pasted into a many containment reference with * those objects already in that reference with which they collide. This * is used in the resolution of * {@linkplain #handleCollision(EReference, List, EObject, ObjectInfo) collisions} * by {@linkplain PasteAction#MERGE merging}. * * @param reference the many containment reference to merge objects into * @param targetObjectList the existing objects in the reference collection * @param objectList the objects that we need to merge into the targets * @param objectInfo object info * * @return the merged lists * * @see #handleCollision(EReference, List, EObject, ObjectInfo) * @see PasteAction#MERGE */ protected List mergeLists(EReference reference, List targetObjectList, List objectList, ObjectInfo objectInfo) { //we did this because the original objectList is unmodifiable List mergedList = new ArrayList(); mergedList.addAll(targetObjectList); //Sanity: no point to merge a list into itself if (targetObjectList.equals(objectList)) { return mergedList; } EObject eObject = null; Iterator childIt = objectList.iterator(); while (childIt.hasNext()) { eObject = (EObject) childIt.next(); if (handleCollision(reference, mergedList, eObject, objectInfo)) { mergedList.add(eObject); } } return mergedList; } /** * Merges an elements to be pasted with an existing element that collides. * * @param weakMerge <code>true</code> to perform a * {@linkplain ClipboardUtil#MERGE_HINT_WEAK weak merge}; * <code>false</code> for a * {@linkplain ClipboardUtil#MERGE_HINT_STRONG strong merge} * @param targetEObject the existing object to merge into * @param eObject the object that we need to merge into the target * @param objectInfo the <code>eObject</code>'s object info * * @see #handleCollision(EReference, List, EObject, ObjectInfo) * @see #handleCollision(EReference, EObject, EObject, ObjectInfo) * @see PasteAction#MERGE */ protected void mergeEObjects(boolean weakMerge, EObject targetEObject, EObject eObject, ObjectInfo objectInfo) { //Sanity: no point to merge an object into itself if (targetEObject.equals(eObject)) { return; } MergedEObjectInfo info = (MergedEObjectInfo) getAllMergedElementsMap() .get(eObject); //if it has been merged in the very same target, then just return if ((info != null) && (info.targetEObjects.contains(targetEObject))) { return; } //no record at all, then create new one if (info == null) { info = new MergedEObjectInfo(); info.mergedEObject = eObject; info.mergedEObjectID = getLoadedEObjectID(eObject); addMergedElementEntry(eObject, info); } //In the Clipboard Design document this is how it defines Merge types: //Weak merges: preserve scalar data in the target element's slots. Only // element vector slots are merged. //Strong merges: overwrite scalar data in the target element's slots. // Element vector slots are also merged. //Our interpretation is that scalar == attributes, whereas vector == // references. //keep track of what we merged, we need this to resolve refs at the // post-paste. info.targetEObjects.add(targetEObject); //handle refs first Iterator it = eObject.eClass().getEAllReferences().iterator(); EReference reference = null; while (it.hasNext()) { reference = (EReference) it.next(); if (reference.isChangeable()) { Object unresolvedEObjectValue = eObject.eGet(reference, false); if (FeatureMapUtil.isMany(eObject, reference)) { List childList = (List) unresolvedEObjectValue; if (childList.isEmpty() == false) { List targetObjectList = (List) targetEObject.eGet( reference, true); List mergedList = mergeLists(reference, targetObjectList, childList, objectInfo); // CWD: Don't remove the original -- we're merging! // mergedList.removeAll(targetObjectList); ClipboardSupportUtil.setEObjectList( targetEObject, reference, mergedList); } } else if (unresolvedEObjectValue != null) { EObject targetObjectValue = (EObject) targetEObject.eGet( reference, true); if (handleCollision(reference, targetObjectValue, (EObject) unresolvedEObjectValue, objectInfo)) { ClipboardSupportUtil.setEObject( targetEObject, reference, (EObject) unresolvedEObjectValue); } } } //reference.isChangeable() } //while //now handle attribs if it is a strong merge (default) if (weakMerge == false) { it = eObject.eClass().getEAllAttributes().iterator(); EAttribute attribute = null; while (it.hasNext()) { attribute = (EAttribute) it.next(); ClipboardSupportUtil.setEAttribute(targetEObject, attribute, eObject.eGet(attribute)); } } } /** * Gets the object info meta-data for the object that I am pasting. * * @return my child object's meta-data */ protected ObjectInfo getChildObjectInfo() { return childObjectInfo; } /** * Gets the child paste operation that I am overriding or extending, if I am * an {@linkplain #isAuxiliaryOperation() auxiliary} paste operation. * * @return my main child paste process, or <code>null</code> if none */ protected PasteChildOperation getMainChildPasteProcess() { return mainChildPasteProcess; } /** * Queries whether I am an auxiliary operation. * * @return <code>true</code> if I am an auxiliary paste operation; * <code>false</code>, otherwise */ public boolean isAuxiliaryOperation() { return (getMainChildPasteProcess() != null); } /** * Gets the object meta-data for my copy object's parent, if it was * serialized along with the child. * * @return my copy parent's object info, or <code>null</code> if none */ protected ObjectInfo getCopyParentObjectInfo() { if (copyParentObjectInfo == null) { copyParentObjectInfo = (ObjectInfo) getResourceInfo().objects .get(getChildObjectInfo().copyParentId); } return copyParentObjectInfo; } /** * Queries whether my copy object's parent object that was copied along with * it was a direct container or not. * * @return <code>true</code> if my copy object's parent was copied and was * its direct container; <code>false</code>, otherwise */ protected boolean isCopyParentDirectParent() { return getChildObjectInfo().containerId .equals(getCopyParentObjectInfo().objId); } /** * After pasting has completed, records the element that I have successfully * (or not) pasted from the original copy. * * @param pastedElement the pasted element, or <code>null</code> if pasting * failed */ protected void setPastedElement(EObject pastedElement) { this.pastedElement = pastedElement; } /** * Queries whether the specified object that already exists in the target * resource can be replaced by an element being pasted, in case of a * collision that is resolved by * {@linkplain PasteAction#REPLACE replacement}. * * @param eObject an object in the target model that might be replaced * @return <code>true</code> if it can be replaced; * <code>false</code>, otherwise * * @see PasteAction#REPLACE */ protected boolean canBeReplaced(EObject eObject) { if (!getParentTarget().isResource()) { // we now know that the target is an EObject if (eObject.equals(getParentEObject())) { return false; } if (ClipboardSupportUtil.isChild(eObject, getParentEObject())) { return false; } } return true; } }