blob: 5c22ace5d05daf0dbed30637bcc37758b5082e83 [file] [log] [blame]
package org.eclipse.emf.edapt.declaration.delegation;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.ETypedElement;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.edapt.declaration.EdaptConstraint;
import org.eclipse.emf.edapt.declaration.EdaptOperation;
import org.eclipse.emf.edapt.declaration.EdaptParameter;
import org.eclipse.emf.edapt.declaration.OperationImplementation;
import org.eclipse.emf.edapt.spi.migration.Instance;
import org.eclipse.emf.edapt.spi.migration.Metamodel;
import org.eclipse.emf.edapt.spi.migration.Model;
/**
* {@description}
*
* @author herrmama
* @author $Author$
* @version $Rev$
* @levd.rating YELLOW Hash: 7F5E5E3ACDF093A538D59014F969B364
*/
@EdaptOperation(identifier = "inlineClass", label = "Inline Class", description = "In the metamodel, a class reachable through a single-valued containment reference is inlined. More specifically, its features are moved to the source class of the reference. In the model, the values of these features are moved accordingly.", breaking = true)
public class InlineClass extends OperationImplementation {
/** {@description} */
@EdaptParameter(main = true, description = "The reference to the class to be inlined")
public EReference reference;
/** {@description} */
@EdaptConstraint(restricts = "reference", description = "The reference must not have an opposite")
public boolean checkReference(EReference reference) {
return reference.getEOpposite() == null;
}
/** {@description} */
@EdaptConstraint(restricts = "reference", description = "The multiplicity of the reference must be single-valued")
public boolean checkReferenceSingleValued(EReference reference) {
return !reference.isMany();
}
/** {@description} */
@EdaptConstraint(restricts = "reference", description = "The reference must be containment")
public boolean checkReferenceContainment(EReference reference) {
return reference.isContainment();
}
/** {@description} */
@EdaptConstraint(description = "The class to be inlined must not have sub classes")
public boolean checkInlinedClassNoSubTypes(Metamodel metamodel) {
final EClass inlinedClass = reference.getEReferenceType();
final EList<EClass> subTypes = metamodel.getInverse(inlinedClass,
EcorePackage.eINSTANCE.getEClass_ESuperTypes());
return subTypes.isEmpty();
}
/** {@description} */
@EdaptConstraint(description = "The class to be inlined must not be a type of another reference")
public boolean checkInlinedClassNotTargetedByReference(Metamodel metamodel) {
final EClass inlinedClass = reference.getEReferenceType();
for (final ETypedElement element : metamodel.<ETypedElement> getInverse(
inlinedClass, EcorePackage.eINSTANCE.getETypedElement_EType())) {
if (element instanceof EReference) {
final EReference reference = (EReference) element;
final EReference eOpposite = reference.getEOpposite();
final EList<EStructuralFeature> features = inlinedClass
.getEStructuralFeatures();
if (eOpposite != null && !features.contains(eOpposite)) {
return false;
}
}
}
return true;
}
/** {@inheritDoc} */
@Override
public void execute(Metamodel metamodel, Model model) {
final EClass inlinedClass = reference.getEReferenceType();
final EClass contextClass = reference.getEContainingClass();
final List<EStructuralFeature> features = new ArrayList<EStructuralFeature>(
inlinedClass.getEStructuralFeatures());
// metamodel adaptation
contextClass.getEStructuralFeatures().addAll(features);
for (final EStructuralFeature feature : features) {
if (feature instanceof EReference) {
final EReference reference = (EReference) feature;
if (reference.getEOpposite() != null) {
reference.getEOpposite().setEType(contextClass);
}
}
}
metamodel.delete(reference);
metamodel.delete(inlinedClass);
// model migration
for (final Instance contextElement : model.getAllInstances(contextClass)) {
final Instance inlinedElement = contextElement.unset(reference);
if (inlinedElement != null) {
for (final EStructuralFeature feature : features) {
contextElement.set(feature, inlinedElement.unset(feature));
}
model.delete(inlinedElement);
}
}
}
}