/** * Copyright (c) 2002-2007 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 - Initial API and implementation */ package org.eclipse.emf.edit.command; import java.util.Collection; import java.util.Collections; import java.util.List; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.util.FeatureMap; import org.eclipse.emf.ecore.util.FeatureMapUtil; import org.eclipse.emf.edit.EMFEditPlugin; import org.eclipse.emf.edit.domain.EditingDomain; /** * The initialize copy command is implemented to set the values of an object copy based on those * of the original (copied) object. It is a helper command used by the CopyCommand. * * <p> * An initialize copy command is an {@link OverrideableCommand}. */ public class InitializeCopyCommand extends AbstractOverrideableCommand { public static Command create(EditingDomain domain, Object owner, CopyCommand.Helper copyHelper) { return domain.createCommand(InitializeCopyCommand.class, new CommandParameter(owner, null, copyHelper)); } /** * This caches the label. */ protected static final String LABEL = EMFEditPlugin.INSTANCE.getString("_UI_InitializeCopyCommand_label"); /** * This caches the description. */ protected static final String DESCRIPTION = EMFEditPlugin.INSTANCE.getString("_UI_InitializeCopyCommand_description"); /** * This is the object being copied. */ protected EObject owner; /** * This is the object (copy) being initialized. */ protected EObject copy; /** * This is a map of objects to their copies */ protected CopyCommand.Helper copyHelper; /** * This constructs an instance that will copy the attribute values of value to those of owner. */ public InitializeCopyCommand(EditingDomain domain, EObject owner, CopyCommand.Helper copyHelper) { super(domain, LABEL, DESCRIPTION); this.owner = owner; this.copy = copyHelper.getCopy(owner); this.copyHelper = copyHelper; } /** * This is the object being copied. */ public EObject getOwner() { return owner; } /** * This is the object (copy) being initialized. */ public EObject getCopy() { return copy; } /** * This is the map of objects to their copies. */ public CopyCommand.Helper getCopyHelper() { return copyHelper; } @Override protected boolean prepare() { return owner.eClass().isInstance(copy); } @Override public void doExecute() { copyAttributes(); copyReferences(); } protected Collection<? extends EAttribute> getAttributesToCopy() { return owner.eClass().getEAllAttributes(); } /** * This method will iterate over the attributes of the owner object and set them * accordingly in the copy. */ protected void copyAttributes() { for (EAttribute attribute : getAttributesToCopy()) { if (attribute.isChangeable() && !attribute.isDerived() && (attribute.isMany() || owner.eIsSet(attribute))) { Object value = owner.eGet(attribute); if (!attribute.isMany()) { copy.eSet(attribute, value); } else { @SuppressWarnings("unchecked") List<Object> list = (List<Object>)copy.eGet(attribute); if (FeatureMapUtil.isFeatureMap(attribute)) { FeatureMap featureMap = (FeatureMap)(List<?>)list; LOOP: for (FeatureMap.Entry entry : (FeatureMap)value) { EStructuralFeature entryFeature = entry.getEStructuralFeature(); if (entryFeature instanceof EAttribute) { featureMap.add(entry); } else { EReference reference = (EReference)entryFeature; EReference reverseReference = reference.getEOpposite(); Object entryValue = entry.getValue(); boolean copiedTargetRequired = reverseReference != null || reference.isContainment(); EObject target = copyHelper.getCopyTarget((EObject)entryValue, copiedTargetRequired); if (target != null) { if (reverseReference != null) { for (FeatureMap.Entry copyEntry : featureMap) { if (copyEntry.getEStructuralFeature() == reference && copyEntry.getValue() == target) { featureMap.move(featureMap.size() - 1, copyEntry); continue LOOP; } } } featureMap.add(reference, target); } } } } else { list.addAll((List<?>)value); } } } } } protected Collection<? extends EReference> getReferencesToCopy() { return owner.eClass().getEAllReferences(); } /** * This method will iterate over the references of the owner object and sets them. * accordingly in the copy. */ protected void copyReferences() { for (EReference reference : getReferencesToCopy()) { if (!reference.isChangeable() || reference.isDerived() || !owner.eIsSet(reference)) { continue; } EReference reverseReference = reference.getEOpposite(); Object value = owner.eGet(reference); if (value == null) { // It must be an unsettable feature to be null and considered set. // copy.eSet(reference, null); continue; } boolean copiedTargetRequired = reverseReference != null || reference.isContainment(); if (reference.isMany()) { @SuppressWarnings("unchecked") List<EObject> valueList = (List<EObject>)value; @SuppressWarnings("unchecked") EList<EObject> copyList = (EList<EObject>)copy.eGet(reference); if (valueList.isEmpty()) { // It must be an unsettable feature to be empty and considered set. // copyList.clear(); } else { int index = 0; for (EObject item : valueList) { EObject target = copyHelper.getCopyTarget(item, copiedTargetRequired); if (target == null) break; // if one is null, they'll all be null if (reverseReference != null) { int position = copyList.indexOf(target); if (position == -1) { copyList.add(index, target); } else { copyList.move(index, target); } } else { copyList.add(target); } ++index; } } } else { EObject target = copyHelper.getCopyTarget((EObject) value, copiedTargetRequired); if (target != null) { copy.eSet(reference, target); } } } } @Override public void doUndo() { // no-op } @Override public void doRedo() { // no-op } @Override public Collection<?> doGetResult() { return Collections.singleton(copy); } @Override public Collection<?> doGetAffectedObjects() { return Collections.singleton(copy); } /** * This gives an abbreviated name using this object's own class' name, without package qualification, * followed by a space separated list of <tt>field:value</tt> pairs. */ @Override public String toString() { StringBuffer result = new StringBuffer(super.toString() + ")"); result.append(" (domain: " + domain + ")"); result.append(" (owner: " + owner + ")"); result.append(" (copy: " + copy + ")"); result.append(" (copyHelper: " + copyHelper + ")"); return result.toString(); } }