blob: 34004f3b85bf6c118ff09c59a8b339c78534c9f3 [file] [log] [blame]
-- Transformation model parameters:
-- OCL - A CompleteOCL document
-- QVTi - The output QVTi model
-- Transformation variables
-- traceabilityPropName - the name of the traceability property
import 'ocl2qvtiUtils.eol';
pre {
var LEFTCS_PACKAGE = OCL!Package.all.first();
var RIGHT = "rightAS";
var LEFT = "leftCS";
var emfTool = new Native("org.eclipse.epsilon.emc.emf.tools.EmfTool");
-- transformation begins --
var oclModel = OCL!Model.all.first();
var qvtiModel = new QVTi!ImperativeModel;
qvtiModel.externalURI = oclModel.externalURI.replace(".ocl",".qvtp.qvtias"); -- FIXME Assuming it will be serialised in the same folder
qvtiModel.ownedImports ::= oclModel.ownedImports;
var qvtiPackage = new QVTi!Package;
qvtiPackage.name = "";
var qvtiTransf = new QVTi!Transformation;
qvtiTransf.name = qvtiModel.name.replace("\\.","_"); // FIXME . as part of the name is causing issues in the CG
qvtiTransf.modelParameter.addAll(QVTi!TypedModel.all());
var importedPckgs = Sequence{};
for (namespace in OCL!Import.all.importedNamespace) {
if (namespace.isKindOf(OCL!Model)) {
importedPckgs.addAll(namespace.ownedPackages);
} else if (namespace.isKindOf(OCL!Package)) {
importedPckgs.add(namespace);
} else {
namespace.println("[WARNING] imported namespace not recognised: ");
}
}
var typedModel = new QVTi!TypedModel;
typedModel.name = LEFT;
typedModel.usedPackage.add(
importedPckgs.selectOne(x
| x.name = OCL!ShadowExp.all.first().getExpressionContextType().owningPackage.name));
qvtiTransf.modelParameter.add(typedModel);
typedModel = new QVTi!TypedModel;
typedModel.name = RIGHT;
typedModel.usedPackage.add(
importedPckgs.selectOne(x
| x.name = OCL!ShadowExp.all.first().type.owningPackage.name));
qvtiTransf.modelParameter.add(typedModel);
qvtiPackage.ownedClasses.add(qvtiTransf);
qvtiModel.ownedPackages.add(qvtiPackage);
}
@lazy
rule Import2Import
transform s : OCL!Import
to t : QVTi!Import {
t.name = s.name;
t.importedNamespace = s.importedNamespace;
}
rule ShadowExp2CreationMapping
transform s : OCL!ShadowExp
to t : QVTi!Mapping {
guard {
var containers = s.closure(x|x.eContainer());
return not containers.exists(x | x.isKindOf(OCL!ShadowExp)) -- innerShadowExps dont produce creation mappings
and
// FIXME this is hazardy. Rework
// Ideally lazy transformation on the content of the OCL Model should avoid calling
// this rule on undesired elemenets (imported from other ocl files)
containers.contains(LEFTCS_PACKAGE);
}
t.name = s.getCreationMappingName();
var leftDomain = s.createCreationMapping_LeftDomain();
var rightDomain = s.createCreationMapping_RightDomain();
var leftVar = leftDomain.guardPattern.variable.first();
t.domain.add(leftDomain);
t.domain.add(rightDomain);
var guardPattern = new QVTi!GuardPattern;
var bottomPattern = new QVTi!BottomPattern;
var pAssignment = new QVTi!PropertyAssignment;
var value = new QVTi!VariableExp;
value.referredVariable = rightDomain.bottomPattern.realizedVariable.first();
value.type = value.referredVariable.type;
var slotExpression = new QVTi!VariableExp;
slotExpression.referredVariable = leftVar;
slotExpression.type = slotExpression.referredVariable.type;
pAssignment.value = value;
pAssignment.slotExpression = slotExpression;
pAssignment.targetProperty = slotExpression.type.getTraceabilityProperty();
bottomPattern.assignment.add(pAssignment);
s.updateGuardPattern(guardPattern, leftVar);
t.guardPattern = guardPattern;
t.bottomPattern = bottomPattern;
}
rule ShadowPart2UpdateMapping
transform s : OCL!ShadowPart
to t : QVTi!Mapping {
guard {
// FIXME this is hazardy. Rework
// Ideally lazy transformation on the content of the OCL Model should avoid calling
// this rule on undesired elemenets (imported from other ocl files)
return s.closure(x|x.eContainer()).contains(LEFTCS_PACKAGE);
}
var shadowExp = s.eContainer();
var refProp = s.referredProperty;
t.name = s.getUpdateMappingName();
var rightDomain = shadowExp.createUpdateMapping_RightDomain();
var leftDomain = shadowExp.createUpdateMapping_LeftDomain();
t.domain.add(leftDomain);
t.domain.add(rightDomain);
// Bottom pattern expression : AssignmentExp
var leftVar = leftDomain.guardPattern.variable.first();
var varExpression = new QVTi!VariableExp;
varExpression.referredVariable = leftVar;
varExpression.type = varExpression.referredVariable.type;
var traceabilityProperty = varExpression.type.getTraceabilityProperty();
var astPropCallExp = new QVTi!PropertyCallExp;
astPropCallExp.ownedSource = varExpression;
astPropCallExp.referredProperty = traceabilityProperty;
astPropCallExp.type = astPropCallExp.referredProperty.type;
var asTypeOpCallExp = new QVTi!OperationCallExp;
asTypeOpCallExp.ownedSource = astPropCallExp;
asTypeOpCallExp.referredOperation = getOclAnyOclAsTypeOp();
asTypeOpCallExp.type = shadowExp.type; // The created type by the owning shadowExp
var argTypeExp = new QVTi!TypeExp;
argTypeExp.referredType = shadowExp.type;
argTypeExp.type = getOclMetaClass();
asTypeOpCallExp.ownedArguments.add(argTypeExp);
var pAssignment = new QVTi!PropertyAssignment;
pAssignment.targetProperty = refProp;
pAssignment.value = s.createPropertyAssignmentValue(leftVar);
pAssignment.slotExpression = asTypeOpCallExp;
var guardPattern = new QVTi!GuardPattern;
var bottomPattern = new QVTi!BottomPattern;
bottomPattern.assignment.add(pAssignment);
shadowExp.updateGuardPattern(guardPattern, leftVar);
t.guardPattern = guardPattern;
t.bottomPattern = bottomPattern;
}
operation OCL!ShadowExp createCreationMapping_LeftDomain ( ) : QVTi!CoreDomain {
var contextType = self.getExpressionContextType();
var domain = new QVTi!CoreDomain;
domain.typedModel = QVTi!TypedModel.all.selectOne(x | x.name= LEFT);
domain.isCheckable = true;
var guardPattern = new QVTi!GuardPattern;
var bottomPattern = new QVTi!BottomPattern;
var variable = new QVTi!Variable;
variable.name = contextType.name.firstToLowerCase();
variable.type = contextType;
guardPattern.variable.add(variable);
domain.guardPattern = guardPattern;
domain.bottomPattern = bottomPattern;
return domain;
}
operation OCL!ShadowExp createCreationMapping_RightDomain () : QVTi!CoreDomain {
var constructedType = self.type;
var domain = new QVTi!CoreDomain;
domain.typedModel = QVTi!TypedModel.all.selectOne(x | x.name= RIGHT);
domain.isEnforceable = true;
var guardPattern = new QVTi!GuardPattern;
var bottomPattern = new QVTi!BottomPattern;
var variable = new QVTi!RealizedVariable;
variable.name = constructedType.name.firstToLowerCase();
variable.type = constructedType;
bottomPattern.realizedVariable.add(variable);
domain.guardPattern = guardPattern;
domain.bottomPattern = bottomPattern;
return domain;
}
operation OCL!ShadowExp createUpdateMapping_LeftDomain ( ) : QVTi!CoreDomain {
// It's exactly the same domain as we have for the creation mappings
return self.createCreationMapping_LeftDomain();
}
operation OCL!ShadowExp createUpdateMapping_RightDomain () : QVTi!CoreDomain {
var constructedType = self.type;
var domain = new QVTi!CoreDomain;
domain.typedModel = QVTi!TypedModel.all.selectOne(x | x.name= RIGHT);
domain.isEnforceable = true;
var guardPattern = new QVTi!GuardPattern;
var bottomPattern = new QVTi!BottomPattern;
domain.guardPattern = guardPattern;
domain.bottomPattern = bottomPattern;
return domain;
}
operation OCL!ShadowPart createPropertyAssignmentValue(leftVar : QVTi!Variable) : OCL!OCLExpression {
-- FIXME what happens with synthetised types ????
var initExp = self.ownedInit;
var eUtil = emfTool.getEcoreUtil();
var newInitExp = eUtil.copy(initExp);
-- We need to replace the OCL refered "self" varible by the QVTi domain "leftVar" and ast op calls
return newInitExp.doReplacements(leftVar);
}
operation OCL!OCLExpression doReplacements(leftVar : QVTi!Variable) : OCL!OCLExpression {
var eUtil = emfTool.getEcoreUtil();
var result = self;
for (exp in self.getAllContents().including(self)) {
switch (true) {
case exp.isSelfVarExp():
exp.referredVariable = leftVar;
case exp.isAstOpCallExp():
var astPropCallExp = new QVTi!PropertyCallExp;
astPropCallExp.ownedSource = exp.ownedSource;
astPropCallExp.referredProperty = astPropCallExp.ownedSource.type.getTraceabilityProperty();
astPropCallExp.type = astPropCallExp.referredProperty.type;
var castType = exp.type;
var asTypeOpCallExp = new QVTi!OperationCallExp;
asTypeOpCallExp.ownedSource = astPropCallExp;
asTypeOpCallExp.referredOperation = getOclAnyOclAsTypeOp();
asTypeOpCallExp.type = castType;
var argTypeExp = new QVTi!TypeExp;
argTypeExp.referredType = castType;
argTypeExp.type = getOclMetaClass();
asTypeOpCallExp.ownedArguments.add(argTypeExp);
if (result = exp) {
result = asTypeOpCallExp;
} else {
eUtil.replace(exp, asTypeOpCallExp);
}
eUtil.`delete`(exp);
}
}
return result;
}
-- Function which takes into account that the shadow is embedded inside of an IfExp
-- so that the guard pattern have the proper guards associated to the the IfExp
operation OCL!ShadowExp updateGuardPattern(guardPattern : QVTi!GuardPattern, leftVar : QVTi!Variable) {
var container = self.eContainer();
if (container.isKindOf(OCL!IfExp)) {
var eUtil = emfTool.getEcoreUtil();
var condition = container.ownedCondition;
var guardPredicates = Sequence{};
if (container.ownedThen = self) {
guardPredicates.add(eUtil.copy(condition));
} else { // it's the else
guardPredicates.add(condition.createNegatedExpression());
}
container = container.eContainer();
-- FIXME create a cached operation to improve performance
while (container.isKindOf(OCL!IfExp)) {
guardPredicates.add(container.ownedCondition.createNegatedExpression());
container = container.eContainer();
}
-- We need to replace the OCL refered "self" varible by the QVTi domain "leftVar"
for (guardPredicate in guardPredicates.invert() ) {
var predicate = new QVTi!Predicate;
predicate.conditionExpression = guardPredicate.doReplacements(leftVar);
guardPattern.predicate.add(predicate);
}
}
}
operation OCL!OCLExpression createNegatedExpression() : OCL!OCLExpression {
var negatedExpression;
var eUtil = emfTool.getEcoreUtil();
// We don't want to create a 'not not conditionExp'
if (self.isBooleanNotOpCallExp()) {
negatedExpression = eUtil.copy(self.ownedSource);
} else {
negatedExpression = new OCL!OperationCallExp;
negatedExpression.name = 'not';
negatedExpression.referredOperation = getBooleanNotOp();
negatedExpression.type = getBooleanPrimitiveType();
negatedExpression.ownedSource = eUtil.copy(self);
}
return negatedExpression;
}
operation OCL!Element isSelfVarExp() : Boolean {
return self.isKindOf(OCL!VariableExp) and self.referredVariable.name = 'self';
}
operation OCL!Element isAstOpCallExp() : Boolean {
return self.isKindOf(OCL!OperationCallExp) and self.referredOperation.name = 'ast';
}
operation OCL!ShadowPart getAstCalls() : Collection(OCL!OperationCallExpression) {
return self.ownedInit.getAllContents().including(self.ownedInit)
.select(x | x.isKindOf(OCL!OperationCallExp) and x.referredOperation.name = 'ast' );
}
operation OCL!Type getTraceabilityProperty() : OCL!Property {
// return self.ownedAttribute.selectOne(x | x.name = 'ast'); // FIXME can we parameterize the traceability property name ?
return self.asSequence().closure(x | x.superClasses).ownedProperties.flatten().selectOne(x | x.name = traceabilityPropName);
}
operation getOclAnyEqualsOp() : OCL!Operation {
return OclStdLib!Class.all.selectOne(x | x.name = 'OclAny').ownedOperations
.selectOne(x | x.name = '=');
}
operation getOclMetaClass() : OCL!Class {
return OclStdLib!Class.all.selectOne(x | x.name = 'Class');
}
operation getOclAnyOclAsTypeOp() : OCL!Operation {
return OclStdLib!Class.all.selectOne(x | x.name = 'OclAny').ownedOperations
.selectOne(x | x.name = 'oclAsType');
}
operation getBooleanPrimitiveType() : OCL!PrimitiveType {
return OclStdLib!PrimitiveType.all.selectOne(x | x.name = 'Boolean');
}
operation getBooleanNotOp() : OCL!Operation {
return OclStdLib!Class.all.selectOne(x | x.name = 'Boolean').ownedOperations
.selectOne(x | x.name = 'not');
}
operation OCL!OCLExpression isBooleanNotOpCallExp() {
return self.isKindOf(OCL!OperationCallExp) and self.referredOperation = getBooleanNotOp();
}
post {
-- We add all the declaratively created QVTi mappings to the imperatively created QVTi transformation
qvtiTransf.`rule`.addAll(QVTi!Mapping.all());
"OCL 2 QVTp completed".println();
}