| -- 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 typedModel = new QVTi!TypedModel; |
| typedModel.name = LEFT; |
| typedModel.usedPackage.add( |
| OCL!Import.all.importedNamespace.ownedPackages.flatten().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( |
| OCL!Import.all.importedNamespace.ownedPackages.flatten().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 { |
| // 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); |
| } |
| |
| 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; |
| // We don't want to create a 'not not conditionExp' |
| if (self.isBooleanNotOpCallExp()) { |
| negatedExpression = eUtil.copy(self.ownedSource); |
| } else { |
| var eUtil = emfTool.getEcoreUtil(); |
| 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(); |
| } |