/**
* Copyright (c) 2012-2016 Marsha Chechik, Alessio Di Sandro, Michalis Famelis,
* Rick Salay.
* 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:
* Alessio Di Sandro - Implementation.
*/
package edu.toronto.cs.se.modelepedia.operator.patch;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jdt.annotation.NonNull;
import edu.toronto.cs.se.mmint.MMINT;
import edu.toronto.cs.se.mmint.MMINTException;
import edu.toronto.cs.se.mmint.MIDTypeRegistry;
import edu.toronto.cs.se.mmint.mid.EMFInfo;
import edu.toronto.cs.se.mmint.mid.GenericElement;
import edu.toronto.cs.se.mmint.mid.MID;
import edu.toronto.cs.se.mmint.mid.MIDLevel;
import edu.toronto.cs.se.mmint.mid.Model;
import edu.toronto.cs.se.mmint.mid.ModelElement;
import edu.toronto.cs.se.mmint.mid.operator.GenericEndpoint;
import edu.toronto.cs.se.mmint.mid.operator.OperatorInput;
import edu.toronto.cs.se.mmint.mid.operator.impl.ConversionOperatorImpl;
import edu.toronto.cs.se.mmint.mid.reasoning.MIDConstraintChecker;
import edu.toronto.cs.se.mmint.mid.relationship.BinaryModelRel;
import edu.toronto.cs.se.mmint.mid.relationship.Mapping;
import edu.toronto.cs.se.mmint.mid.relationship.MappingReference;
import edu.toronto.cs.se.mmint.mid.relationship.ModelElementReference;
import edu.toronto.cs.se.mmint.mid.relationship.ModelEndpointReference;
import edu.toronto.cs.se.mmint.mid.relationship.ModelRel;
import edu.toronto.cs.se.mmint.mid.utils.FileUtils;
import edu.toronto.cs.se.mmint.mid.utils.MIDRegistry;
import edu.toronto.cs.se.mmint.mid.utils.PrimitiveEObjectWrapper;
public class ModelRelTypeTransformation extends ConversionOperatorImpl {
// input-output
private final static @NonNull String IN_MODEL = "src";
public final static @NonNull String OUT_MODEL = "tgt";
protected final static @NonNull String OUT_MODELREL = "trace";
private final static @NonNull String GENERIC_MODELRELTYPE = "MR";
// constants
protected final static @NonNull String TRANSFORMATION_SUFFIX = "_transformed";
protected EObject tgtRootModelObj;
protected String tgtModelUri;
private void init() {
// state
tgtRootModelObj = null;
}
@SuppressWarnings("unchecked")
protected EObject transformModelObj(ModelEndpointReference srcModelTypeEndpointRef, EObject srcModelObj, Map<EObject, ModelElementReference> srcModelObjs, ModelEndpointReference tgtModelTypeEndpointRef, Map<EObject, EObject> tgtModelObjs) throws MMINTException {
ModelElementReference tgtModelElemTypeRef = srcModelObjs.get(srcModelObj);
EClass tgtModelTypeObj = (EClass) tgtModelElemTypeRef.getObject().getEMFTypeObject();
EObject tgtModelObj = tgtModelTypeObj.getEPackage().getEFactoryInstance().create(tgtModelTypeObj);
EObject srcContainerModelObj = srcModelObj.eContainer();
if (srcContainerModelObj == null) { // root found
tgtRootModelObj = tgtModelObj;
}
else {
EObject tgtContainerModelObj = tgtModelObjs.get(srcContainerModelObj);
if (tgtContainerModelObj == null) {
// recursion
tgtContainerModelObj = transformModelObj(srcModelTypeEndpointRef, srcContainerModelObj, srcModelObjs, tgtModelTypeEndpointRef, tgtModelObjs);
}
// find containment based on model element types first, then fallback to first one available
String srcModelElemTypeContainmentUri = MIDRegistry.getModelAndModelElementUris(srcModelObj.eContainingFeature(), MIDLevel.TYPES)[1];
ModelElementReference srcModelElemTypeContainment = MIDRegistry.getReference(srcModelElemTypeContainmentUri, srcModelTypeEndpointRef.getModelElemRefs());
EReference containmentReference = null, fallbackContainmentReference = null;
for (EReference containment : tgtContainerModelObj.eClass().getEAllContainments()) {
if (MIDConstraintChecker.instanceofEMFClass(tgtModelObj, containment.getEType().getName())) {
String tgtModelElemTypeContainmentUri = MIDRegistry.getModelAndModelElementUris(containment, MIDLevel.TYPES)[1];
ModelElementReference tgtModelElemTypeContainment = MIDRegistry.getReference(tgtModelElemTypeContainmentUri, tgtModelTypeEndpointRef.getModelElemRefs());
if (
tgtModelElemTypeContainment == null ||
srcModelElemTypeContainment.getModelElemEndpointRefs().get(0).eContainer() != tgtModelElemTypeContainment.getModelElemEndpointRefs().get(0).eContainer()
) {
if (fallbackContainmentReference == null) {
fallbackContainmentReference = containment;
}
continue;
}
containmentReference = containment;
break;
}
}
if (containmentReference == null) {
containmentReference = fallbackContainmentReference;
}
Object containment = tgtContainerModelObj.eGet(containmentReference);
if (containment instanceof EList) {
((EList<EObject>) containment).add(tgtModelObj);
}
else {
tgtContainerModelObj.eSet(containmentReference, tgtModelObj);
}
}
tgtModelObjs.put(srcModelObj, tgtModelObj);
return tgtModelObj;
}
@SuppressWarnings("unchecked")
protected void transformModelObjFeature(EObject srcModelObj, String srcFeatureName, EObject tgtModelObj, String tgtFeatureName, List<PrimitiveEObjectWrapper> primitiveSrcModelObjs, List<PrimitiveEObjectWrapper> primitiveTgtModelObjs, Map<EObject, EObject> tgtModelObjs) {
EStructuralFeature srcFeature = srcModelObj.eClass().getEStructuralFeature(srcFeatureName), tgtFeature = tgtModelObj.eClass().getEStructuralFeature(tgtFeatureName);
if (srcFeature instanceof EReference && ((EReference) srcFeature).isContainment()) {
return;
}
try {
Object value = srcModelObj.eGet(srcFeature);
if (srcFeature instanceof EReference) {
if (value instanceof EList<?>) {
for (EObject srcRefModelObj : (EList<EObject>) value) {
EObject tgtRefModelObj = tgtModelObjs.get(srcRefModelObj);
if (tgtRefModelObj == null) {
continue;
}
FileUtils.setModelObjectFeature(tgtModelObj, tgtFeatureName, tgtRefModelObj);
}
}
else {
EObject srcRefModelObj = (EObject) value;
EObject tgtRefModelObj = tgtModelObjs.get(srcRefModelObj);
if (tgtRefModelObj != null) {
FileUtils.setModelObjectFeature(tgtModelObj, tgtFeatureName, tgtRefModelObj);
}
}
}
else { // srcFeature instanceof EAttribute
FileUtils.setModelObjectFeature(tgtModelObj, tgtFeatureName, value);
primitiveSrcModelObjs.add(new PrimitiveEObjectWrapper(srcModelObj, srcFeature, value));
primitiveTgtModelObjs.add(new PrimitiveEObjectWrapper(tgtModelObj, tgtFeature, value));
}
}
catch (MMINTException e) {
MMINTException.print(IStatus.WARNING, "Error transforming model object feature, skipping it", e);
}
}
protected void transform(BinaryModelRel traceModelRel, Model srcModel, int srcIndex, int tgtIndex) throws Exception {
ModelRel traceModelRelType = traceModelRel.getMetatype();
ModelEndpointReference srcModelTypeEndpointRef = traceModelRelType.getModelEndpointRefs().get(srcIndex);
ModelEndpointReference tgtModelTypeEndpointRef = traceModelRelType.getModelEndpointRefs().get(tgtIndex);
Map<EObject, ModelElementReference> srcModelObjs = new LinkedHashMap<EObject, ModelElementReference>();
TreeIterator<EObject> srcModelObjsIter = srcModel.getEMFInstanceRoot().eResource().getAllContents();
// first pass: get model objects to be transformed
while (srcModelObjsIter.hasNext()) {
EObject srcModelObj = srcModelObjsIter.next();
ModelElement srcModelElemType = MIDConstraintChecker.getAllowedModelElementType(traceModelRel.getModelEndpointRefs().get(0), srcModelObj);
if (srcModelElemType == null) {
continue;
}
ModelElementReference srcModelElemTypeRef = MIDRegistry.getReference(srcModelElemType.getUri(), srcModelTypeEndpointRef.getModelElemRefs());
ModelElementReference tgtModelElemTypeRef = ((MappingReference) srcModelElemTypeRef.getModelElemEndpointRefs().get(0).eContainer()).getModelElemEndpointRefs().get(tgtIndex).getModelElemRef();
srcModelObjs.put(srcModelObj, tgtModelElemTypeRef);
}
// second pass: transform
Map<EObject, EObject> tgtModelObjs = new LinkedHashMap<EObject, EObject>();
for (EObject srcModelObj : srcModelObjs.keySet()) {
if (tgtModelObjs.get(srcModelObj) != null) { // already transformed
continue;
}
transformModelObj(srcModelTypeEndpointRef, srcModelObj, srcModelObjs, tgtModelTypeEndpointRef, tgtModelObjs);
}
// third pass: EAttributes and non-containment EReferences
List<PrimitiveEObjectWrapper> primitiveSrcModelObjs = new ArrayList<PrimitiveEObjectWrapper>(), primitiveTgtModelObjs = new ArrayList<PrimitiveEObjectWrapper>();
for (Map.Entry<EObject, EObject> tgtModelObjsEntry : tgtModelObjs.entrySet()) {
EObject srcModelObj = tgtModelObjsEntry.getKey(), tgtModelObj = tgtModelObjsEntry.getValue();
for (ModelElementReference srcModelElemTypeRef : srcModelTypeEndpointRef.getModelElemRefs()) {
EMFInfo srcModelElemTypeEInfo = srcModelElemTypeRef.getObject().getEInfo();
if (
srcModelElemTypeEInfo.getFeatureName() == null ||
!MIDConstraintChecker.instanceofEMFClass(srcModelObj, srcModelElemTypeEInfo.getClassName())
) {
continue;
}
ModelElementReference tgtModelElemTypeRef = ((MappingReference) srcModelElemTypeRef.getModelElemEndpointRefs().get(0).eContainer()).getModelElemEndpointRefs().get(tgtIndex).getModelElemRef();
EMFInfo tgtModelElemTypeEInfo = tgtModelElemTypeRef.getObject().getEInfo();
transformModelObjFeature(srcModelObj, srcModelElemTypeEInfo.getFeatureName(), tgtModelObj, tgtModelElemTypeEInfo.getFeatureName(), primitiveSrcModelObjs, primitiveTgtModelObjs, tgtModelObjs);
}
}
for (int i = 0; i < primitiveSrcModelObjs.size(); i++) { // needed to avoid concurrent modification of tgtModelObjs
tgtModelObjs.put(primitiveSrcModelObjs.get(i), primitiveTgtModelObjs.get(i));
}
// fourth pass: create model elements and links
FileUtils.writeModelFile(tgtRootModelObj, tgtModelUri, true);
for (Map.Entry<EObject, EObject> tgtModelObjEntry : tgtModelObjs.entrySet()) {
EList<ModelElementReference> targetModelElemRefs = new BasicEList<ModelElementReference>();
ModelElementReference srcModelElemRef = traceModelRel.getModelEndpointRefs().get(0).createModelElementInstanceAndReference(tgtModelObjEntry.getKey(), null);
targetModelElemRefs.add(srcModelElemRef);
ModelElementReference tgtModelElemRef = traceModelRel.getModelEndpointRefs().get(1).createModelElementInstanceAndReference(tgtModelObjEntry.getValue(), null);
targetModelElemRefs.add(tgtModelElemRef);
Mapping mappingType = MIDTypeRegistry.getType(MIDConstraintChecker.getAllowedMappingTypeReferences(traceModelRelType, srcModelElemRef, tgtModelElemRef).get(0));
MappingReference newMappingRef = mappingType.createInstanceAndReferenceAndEndpointsAndReferences(true, targetModelElemRefs);
newMappingRef.getObject().setName(srcModelElemRef.getObject().getName() + MMINT.BINARY_MODELREL_MAPPING_SEPARATOR + tgtModelElemRef.getObject().getName());
}
}
@Override
public Map<String, Model> run(Map<String, Model> inputsByName,
java.util.Map<String, GenericElement> genericsByName, Map<String, MID> outputMIDsByName)
throws Exception {
// input
ModelRel traceModelRelType = (ModelRel) genericsByName.get(GENERIC_MODELRELTYPE);
Model srcModel = inputsByName.get(IN_MODEL);
this.init();
int srcIndex = (
traceModelRelType instanceof BinaryModelRel ||
MIDConstraintChecker.isAllowedModelEndpoint(traceModelRelType.getModelEndpointRefs().get(0), srcModel, new HashMap<String, Integer>())
) ?
0 : 1;
int tgtIndex = 1 - srcIndex;
Model tgtModelType = traceModelRelType.getModelEndpointRefs().get(tgtIndex).getObject().getTarget();
tgtModelUri = FileUtils.getUniqueUri(
FileUtils.replaceFileExtensionInUri(
FileUtils.addFileNameSuffixInUri(srcModel.getUri(), TRANSFORMATION_SUFFIX),
tgtModelType.getFileExtension()),
true,
false);
Model tgtModel = tgtModelType.createInstance(tgtModelUri, outputMIDsByName.get(OUT_MODEL));
BinaryModelRel traceModelRel = traceModelRelType.createBinaryInstance(null, outputMIDsByName.get(OUT_MODELREL));
traceModelRel.setName(srcModel.getName() + MMINT.BINARY_MODELREL_MAPPING_SEPARATOR + tgtModel.getName());
traceModelRelType.getModelEndpointRefs().get(srcIndex).getObject().createInstance(srcModel, traceModelRel);
traceModelRelType.getModelEndpointRefs().get(tgtIndex).getObject().createInstance(tgtModel, traceModelRel);
transform(traceModelRel, srcModel, srcIndex, tgtIndex);
tgtModel.createInstanceEditor();
// output
Map<String, Model> outputsByName = new HashMap<>();
outputsByName.put(OUT_MODEL, tgtModel);
outputsByName.put(OUT_MODELREL, traceModelRel);
return outputsByName;
}
@Override
public boolean isAllowedGeneric(GenericEndpoint genericTypeEndpoint, GenericElement genericType, EList<OperatorInput> inputs) throws MMINTException {
if (!super.isAllowedGeneric(genericTypeEndpoint, genericType, inputs)) {
return false;
}
ModelRel modelRelType = (ModelRel) genericType;
// check 1: satisfies transformation constraint
if (!(new ModelRelTypeTransformationConstraint().validate(modelRelType))) {
return false;
}
Model srcModel = inputs.get(0).getModel();
// check 2: allowed source model
if (
!MIDConstraintChecker.isAllowedModelEndpoint(modelRelType.getModelEndpointRefs().get(0), srcModel, new HashMap<String, Integer>()) && (
modelRelType instanceof BinaryModelRel || // mandatory direction
!MIDConstraintChecker.isAllowedModelEndpoint(modelRelType.getModelEndpointRefs().get(1), srcModel, new HashMap<String, Integer>())
)
) {
return false;
}
return true;
}
}