/* * Copyright (c) 2006, 2009 Borland Software Corporation * * 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: * Michael Golubev (Borland) - initial API and implementation * Artem Tikhomirov (Borland) - copy elements with cross-references */ package org.eclipse.gmf.internal.common.reconcile; import java.util.Collection; import java.util.Collections; import java.util.Map; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.EStructuralFeature.Setting; import org.eclipse.emf.ecore.util.EcoreUtil; public interface Copier { /** * Copies relevant content of the old object into the current ancestor. * * @param currentParent * the parent node in the current tree. It is guarranteed that * parent was matched with <code>old.eContainer()</code> * @param old * the element found in the old tree which does not have matched * element in the current tree * @param reconciler TODO * @return just created element copied into the current tree or * <code>null</code> if copier decided to avoid creation of * element. * * NOTE: in case if this method returns created element, result is * considered as "matched" with <code>old</code> automatically, without * any further matchings */ public EObject copyToCurrent(EObject currentParent, EObject old, Reconciler reconciler); public static final Copier NEVER_COPY = new Copier(){ public EObject copyToCurrent(EObject currentParent, EObject old, Reconciler reconciler) { return null; } }; public static final Copier COMPLETE_COPY_NO_CROSSREF = new Copier() { public EObject copyToCurrent(EObject currentParent, EObject old, Reconciler reconciler) { safetyCheck(old); EClass currentParentEClass = currentParent.eClass(); EObject oldParent = old.eContainer(); EClass oldParentEClass = oldParent.eClass(); EObject currentCopy = null; if (currentParentEClass.equals(oldParentEClass)) { currentCopy = EcoreUtil.copy(old); EStructuralFeature containment = old.eContainingFeature(); Object currentValue = currentParent.eGet(containment); if (currentValue instanceof Collection<?>) { @SuppressWarnings("unchecked") Collection<Object> asCollection = (Collection<Object>) currentValue; asCollection.add(currentCopy); } else { currentParent.eSet(containment, currentCopy); } } return currentCopy; } /** * It is not trivial to copy the external references while reconciling. If you need this, do not use this simple implementation. */ private void safetyCheck(EObject old) { if (!EcoreUtil.CrossReferencer.find(Collections.singleton(old)).isEmpty()) { throw new IllegalArgumentException("I am not intended to copy elements with cross references"); } } }; // XXX for now, keep this new implementation separate, however, won't hurt to combine it with the old one public static final Copier COMPLETE_COPY_WITH_CROSSREF = new WithCrossRefsCopier(); public static class WithCrossRefsCopier implements Copier { public EObject copyToCurrent(EObject currentParent, EObject old, Reconciler reconciler) { final Map<EObject, Collection<Setting>> crossReferences = EcoreUtil.CrossReferencer.find(Collections.singleton(old)); EClass currentParentEClass = currentParent.eClass(); EObject oldParent = old.eContainer(); EClass oldParentEClass = oldParent.eClass(); EObject currentCopy = null; if (currentParentEClass.equals(oldParentEClass)) { currentCopy = EcoreUtil.copy(old); EStructuralFeature containment = old.eContainingFeature(); Object currentValue = currentParent.eGet(containment); if (currentValue instanceof Collection<?>) { @SuppressWarnings("unchecked") Collection<Object> asCollection = (Collection<Object>) currentValue; asCollection.add(currentCopy); } else { currentParent.eSet(containment, currentCopy); } reconciler.registerCrossReferencesToUpdate(crossReferences); } return currentCopy; } }; }