[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[henshin-user] Issue with (and fix for) EGraph.copy

Hi,

I've been trying to run some transformations over meta-models (i.e., instances of Ecore). As part of this, I needed to copy EGraphs and used EGraph.copy. This crashed with an NPE.

I did some digging and found out that the reason is that there is an issue with how EcoreUtil.Copier works and how types and generic types are handled in Ecore. Details here: https://stackoverflow.com/questions/46323548/copying-egenerictype-instances-with-ecoreutil-copier As a result, when calling EGraph.copy (null), in some cases the code can then not find copies of the EGenericType instances in the resulting map and adding them to the new graph crashes.

I have written a slightly extended version of EcoreUtil.Copier which I attach. I have so far used this like in the code below, but was wondering whether there's an interest to include this directly into Henshin instead.

// Given a graph graph1 with some Ecore instances in it
EcoreCopier copier = new EcoreCopier();
copier.copyAll(graph1.getRoots());
copier.copyReferences();

EGraph graph2 = graph1.copy(copier);

Best regards,

Steffen

--
Dr. rer. nat. Steffen Zschaler AHEA
Senior Lecturer

King's College London
Department of Informatics

Email szschaler@xxxxxxx
Phone +44 (020) 7848 1513
WWW   http://www.steffen-zschaler.de/

import java.util.LinkedHashMap;
import java.util.Map;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EGenericType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.util.EcoreUtil.Copier;

/**
 * A copier that correctly handles copying of types and generic types in
 * {@link ETypedElement} instances.
 *
 * @author Steffen Zschaler
 */
@SuppressWarnings("serial")
public class EcoreCopier extends Copier {

	public EcoreCopier() {
		super();
	}

	public EcoreCopier(boolean resolveProxies) {
		super(resolveProxies);
	}

	public EcoreCopier(boolean resolveProxies, boolean useOriginalReferences) {
		super(resolveProxies, useOriginalReferences);
	}

	/**
	 * Does everything the super method does, but also makes sure implicitly created
	 * types are stored in the copy map.
	 *
	 * @see org.eclipse.emf.ecore.util.EcoreUtil.Copier#copyContainment(org.eclipse.emf.ecore.EReference,
	 *      org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject)
	 */
	@Override
	protected void copyContainment(EReference eReference, EObject eObject, EObject copyEObject) {
		super.copyContainment(eReference, eObject, copyEObject);

		if (eObject.eIsSet(eReference)) {
			if (eReference == EcorePackage.Literals.ETYPED_ELEMENT__EGENERIC_TYPE) {
				// If this object has an actual generic type, setting it in the copy will have
				// created a copy of the `eType`. We need to make sure that copy shows up in our
				// map
				// TODO: Probably needs more testing: we really would want to make sure this is
				// wired up to the type we may already have copied, need to make sure this is
				// happening...
				if (!containsKey((EObject) eObject.eGet(EcorePackage.Literals.ETYPED_ELEMENT__ETYPE, resolveProxies))) {
					put((EObject) eObject.eGet(EcorePackage.Literals.ETYPED_ELEMENT__ETYPE, resolveProxies),
							(EObject) copyEObject.eGet(EcorePackage.Literals.ETYPED_ELEMENT__ETYPE, resolveProxies));
				}
			} else if (eReference == EcorePackage.Literals.ECLASS__EGENERIC_SUPER_TYPES) {
				for (int i = 0, size = ((EClass) eObject).getESuperTypes().size(); i < size; i++) {
					EClass superType = ((EClass) eObject).getESuperTypes().get(i);
					if (!containsKey(superType)) {
						put(superType, ((EClass) copyEObject).getESuperTypes().get(i));
					}
				}
			}
		}
	}

	/**
	 * Used to store copies created when copying references
	 */
	private Map<EObject, EObject> lateCopies;

	/**
	 * Does everything the super method does, but treats eType and eGenericType
	 * specially.
	 *
	 * @see org.eclipse.emf.ecore.util.EcoreUtil.Copier#copyReference(org.eclipse.emf.ecore.EReference,
	 *      org.eclipse.emf.ecore.EObject, org.eclipse.emf.ecore.EObject)
	 */
	@Override
	protected void copyReference(EReference eReference, EObject eObject, EObject copyEObject) {
		super.copyReference(eReference, eObject, copyEObject);

		if (eObject.eIsSet(eReference)) {
			if (eReference == EcorePackage.Literals.ETYPED_ELEMENT__ETYPE) {
				// In this case, this will have implicitly copied the generic type, so need to
				// add it to the map
				lateCopies.put(
						(EObject) eObject.eGet(EcorePackage.Literals.ETYPED_ELEMENT__EGENERIC_TYPE, resolveProxies),
						(EObject) copyEObject.eGet(EcorePackage.Literals.ETYPED_ELEMENT__EGENERIC_TYPE,
								resolveProxies));
			}
			else if (eReference == EcorePackage.Literals.ECLASS__ESUPER_TYPES) {
				for (int i = 0, size = ((EClass)eObject).getEGenericSuperTypes().size(); i < size; i++) {
					EGenericType eSuperGeneric = ((EClass)eObject).getEGenericSuperTypes().get(i);
					lateCopies.put (eSuperGeneric, ((EClass)copyEObject).getEGenericSuperTypes().get(i));
				}
			}
		}
	}

	/*
	 * Need to make sure that we don't change the copy map while
	 * super.copyReferences is iterating over it
	 *
	 * @see org.eclipse.emf.ecore.util.EcoreUtil.Copier#copyReferences()
	 */
	@Override
	public void copyReferences() {
		lateCopies = new LinkedHashMap<>();

		super.copyReferences();

		putAll(lateCopies);
		lateCopies = null;
	}
}