blob: ee64f02f22889d2b666dbbb8290bf16fa98b1a4c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2010 BMW Car IT, Technische Universitaet Muenchen, and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* BMW Car IT - Initial API and implementation
* Technische Universitaet Muenchen - Major refactoring and extension
*******************************************************************************/
package org.eclipse.emf.edapt.internal.migration.internal;
import java.text.MessageFormat;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.Enumerator;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.edapt.internal.common.EcoreUtils;
import org.eclipse.emf.edapt.internal.common.ResourceUtils;
import org.eclipse.emf.edapt.internal.common.ReversableMap;
import org.eclipse.emf.edapt.internal.common.TwoWayIdentityHashMap;
import org.eclipse.emf.edapt.spi.migration.AttributeSlot;
import org.eclipse.emf.edapt.spi.migration.Instance;
import org.eclipse.emf.edapt.spi.migration.Model;
import org.eclipse.emf.edapt.spi.migration.ModelResource;
import org.eclipse.emf.edapt.spi.migration.ReferenceSlot;
import org.eclipse.emf.edapt.spi.migration.Slot;
import org.eclipse.emf.edapt.spi.migration.Type;
/**
* Convert a model graph to an EMF model.
*
* @author herrmama
* @author $Author$
* @version $Rev$
* @levd.rating YELLOW Hash: 4D9E84B44F84E9C760AB3E79983988B4
*/
public class BackwardConverter {
/** Mapping from graph nodes to EMF model elements. */
protected ReversableMap<Instance, EObject> mapping;
/** Convert model graph to EMF elements. */
public ResourceSet convert(Model model) {
model.getMetamodel().refreshCaches();
mapping = new TwoWayIdentityHashMap<Instance, EObject>();
initObjects(model);
final ResourceSet resourceSet = initResources(model);
initProperties(model);
adjustUUIDs(model);
return resourceSet;
}
/** Create an EMF model element for each node. */
protected void initObjects(Model model) {
for (final Type type : model.getTypes()) {
createObjects(type);
}
}
/** Create all EMF model elements of a certain type. */
protected void createObjects(Type type) {
final EClass sourceClass = type.getEClass();
final EClass targetClass = resolveEClass(sourceClass);
for (final Instance element : type.getInstances()) {
final EObject eObject = EcoreUtil.create(targetClass);
if (element.isProxy()) {
((InternalEObject) eObject).eSetProxyURI(element.getUri());
}
mapping.put(element, eObject);
}
}
/** Resolve the class to which an instance should be converted. */
protected EClass resolveEClass(EClass eClass) {
return eClass;
}
/** Determine root EMF model elements. */
protected ResourceSet initResources(Model model) {
final ResourceSet resourceSet = new ResourceSetImpl();
ResourceUtils.register(model.getMetamodel().getEPackages(),
resourceSet.getPackageRegistry());
for (final ModelResource modelResource : model.getResources()) {
final Resource resource = resourceSet.createResource(modelResource
.getUri());
if (resource instanceof XMLResource) {
final XMLResource xmlResource = (XMLResource) resource;
if (modelResource.getEncoding() != null) {
xmlResource.setEncoding(modelResource.getEncoding());
}
}
for (final Instance element : modelResource.getRootInstances()) {
resource.getContents().add(resolve(element));
}
}
return resourceSet;
}
/** Initialize the EMF model elements based on the edges. */
protected void initProperties(Model model) {
for (final Type type : model.getTypes()) {
for (final Instance instance : type.getInstances()) {
initProperties(instance);
}
}
}
/** Initialize an EMF model element based on the edges outgoing from a node. */
@SuppressWarnings({ "unchecked", "rawtypes" })
protected void initProperties(Instance element) {
final EObject eObject = resolve(element);
for (final Slot slot : element.getSlots()) {
final EStructuralFeature sourceFeature = slot.getEFeature();
final EStructuralFeature targetFeature = resolveFeature(sourceFeature);
if (ignore(sourceFeature)) {
continue;
}
if (slot instanceof AttributeSlot) {
if (sourceFeature.getEType() instanceof EEnum) {
final AttributeSlot attributeSlot = (AttributeSlot) slot;
if (sourceFeature.isMany()) {
final EList values = (EList) eObject.eGet(targetFeature);
for (final Object value : attributeSlot.getValues()) {
values.add(resolveLiteral(value));
}
} else {
if (!attributeSlot.getValues().isEmpty()) {
eObject.eSet(targetFeature,
resolveLiteral(attributeSlot.getValues().get(0)));
}
}
} else {
eObject.eSet(targetFeature, element.get(sourceFeature));
}
} else {
final ReferenceSlot referenceSlot = (ReferenceSlot) slot;
if (sourceFeature.isMany()) {
final EList values = (EList) eObject.eGet(targetFeature);
int index = 0;
for (final Instance value : referenceSlot.getValues()) {
final EObject valueEObject = resolve(value);
if (sourceFeature.isUnique()
&& values.contains(valueEObject)) {
values.move(index, valueEObject);
} else {
values.add(index, valueEObject);
}
index++;
}
} else {
if (!referenceSlot.getValues().isEmpty()) {
eObject.eSet(targetFeature, resolve(referenceSlot
.getValues().get(0)));
}
}
}
}
}
/**
* Adjust UUIDs in EMF model.
* Needs to be done after containment hierarchy in EMF model is established
* (done in {@link #initProperties(Model)}).
*/
protected void adjustUUIDs(Model model) {
for (final Type type : model.getTypes()) {
for (final Instance instance : type.getInstances()) {
final String uuid = instance.getUuid();
if (uuid != null) {
final EObject eObject = resolve(instance);
EcoreUtils.setUUID(eObject, uuid);
}
}
}
}
/** Resolve the feature to which a value should be transferred. */
protected EStructuralFeature resolveFeature(EStructuralFeature feature) {
return feature;
}
/** Resolve the literal value of an enumeration. */
protected Enumerator resolveLiteral(Object literal) {
if (EEnumLiteral.class.isInstance(literal)) {
return EEnumLiteral.class.cast(literal);
} else if (Enumerator.class.isInstance(literal)) {
return Enumerator.class.cast(literal);
}
throw new IllegalArgumentException(MessageFormat.format(
"Unexpected literal {0} of type {1} cannot be converted to an Enumerator", literal, literal.getClass())); //$NON-NLS-1$
}
/**
* Determines whether a certain feature should be ignored during conversion.
*/
protected boolean ignore(EStructuralFeature feature) {
return feature.isTransient()
|| !feature.isChangeable()
||
// according to
// http://www.eclipse.org/newsportal/article.php?id=26780&group=eclipse.tools.emf
// the following three references need to be ignored
EcorePackage.eINSTANCE.getEClass_ESuperTypes().equals(feature)
|| EcorePackage.eINSTANCE.getETypedElement_EType().equals(
feature)
|| EcorePackage.eINSTANCE.getEOperation_EExceptions().equals(
feature);
}
/** Get the EMF model element corresponding to a node. */
protected EObject resolve(Instance instance) {
return mapping.get(instance);
}
}