| package org.eclipse.qvtd.cs2as.compiler.internal; |
| |
| import static org.eclipse.qvtd.cs2as.compiler.internal.OCL2QVTpUtil.firstToLowerCase; |
| import static org.eclipse.qvtd.cs2as.compiler.internal.OCL2QVTpUtil.getAllContainers; |
| import static org.eclipse.qvtd.cs2as.compiler.internal.OCL2QVTpUtil.getAllContents; |
| import static org.eclipse.qvtd.cs2as.compiler.internal.OCL2QVTpUtil.getAllContentsIncludingSelf; |
| import static org.eclipse.qvtd.cs2as.compiler.internal.OCL2QVTpUtil.getCreationMappingName; |
| import static org.eclipse.qvtd.cs2as.compiler.internal.OCL2QVTpUtil.getExpressionContextType; |
| import static org.eclipse.qvtd.cs2as.compiler.internal.OCL2QVTpUtil.getSuperClasses; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.function.Function; |
| import java.util.function.Predicate; |
| import java.util.logging.Logger; |
| import java.util.stream.Collectors; |
| |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.ocl.pivot.Class; |
| import org.eclipse.ocl.pivot.IfExp; |
| import org.eclipse.ocl.pivot.Import; |
| import org.eclipse.ocl.pivot.Model; |
| import org.eclipse.ocl.pivot.Namespace; |
| import org.eclipse.ocl.pivot.OCLExpression; |
| import org.eclipse.ocl.pivot.Operation; |
| import org.eclipse.ocl.pivot.OperationCallExp; |
| import org.eclipse.ocl.pivot.Package; |
| import org.eclipse.ocl.pivot.PivotFactory; |
| import org.eclipse.ocl.pivot.Property; |
| import org.eclipse.ocl.pivot.PropertyCallExp; |
| import org.eclipse.ocl.pivot.ShadowExp; |
| import org.eclipse.ocl.pivot.ShadowPart; |
| import org.eclipse.ocl.pivot.Type; |
| import org.eclipse.ocl.pivot.TypeExp; |
| import org.eclipse.ocl.pivot.Variable; |
| import org.eclipse.ocl.pivot.VariableExp; |
| import org.eclipse.ocl.pivot.utilities.EnvironmentFactory; |
| import org.eclipse.qvtd.pivot.qvtbase.QVTbaseFactory; |
| import org.eclipse.qvtd.pivot.qvtbase.Transformation; |
| import org.eclipse.qvtd.pivot.qvtbase.TypedModel; |
| import org.eclipse.qvtd.pivot.qvtcore.CoreModel; |
| import org.eclipse.qvtd.pivot.qvtcore.Mapping; |
| import org.eclipse.qvtd.pivot.qvtcore.QVTcoreFactory; |
| import org.eclipse.qvtd.pivot.qvtcorebase.BottomPattern; |
| import org.eclipse.qvtd.pivot.qvtcorebase.CoreDomain; |
| import org.eclipse.qvtd.pivot.qvtcorebase.GuardPattern; |
| import org.eclipse.qvtd.pivot.qvtcorebase.PropertyAssignment; |
| import org.eclipse.qvtd.pivot.qvtcorebase.QVTcoreBaseFactory; |
| import org.eclipse.qvtd.pivot.qvtcorebase.RealizedVariable; |
| |
| public class OCL2QVTm { |
| |
| private @NonNull Logger logger = Logger.getLogger(getClass().getName()); |
| private @NonNull EnvironmentFactory envFact; |
| private @NonNull String traceabilityPropName; |
| private @Nullable Map<?,?> saveOptions; |
| |
| public static final @NonNull String RIGHT_MODEL_TYPE_NAME = "rightAS"; |
| public static final @NonNull String LEFT_MODEL_TYPE_NAME = "leftCS"; |
| |
| public OCL2QVTm(@NonNull EnvironmentFactory envFact, @NonNull String traceabilityPropName) { |
| this.envFact = envFact; |
| this.traceabilityPropName = traceabilityPropName; |
| } |
| |
| public Resource run(ResourceSet resourceSet, URI oclDocURI) { |
| |
| if (!"oclas".equals(oclDocURI.fileExtension())) { |
| throw new IllegalArgumentException(oclDocURI.toString() + " is not an .oclas URI"); |
| } |
| Resource input = resourceSet.getResource(oclDocURI, true); |
| EObject rootModel = input.getContents().get(0); |
| if (rootModel instanceof Model) { |
| Model model = (Model) rootModel; |
| CoreModel outputModel = oclModelToImperativeModel().apply(model); |
| |
| // We create the output resource |
| URI outputURI = oclDocURI.trimFileExtension().trimFileExtension().appendFileExtension("qvtm"); |
| Resource outputResource = resourceSet.createResource(outputURI); |
| outputResource.getContents().add(outputModel); |
| return outputResource; |
| |
| } else { |
| throw new IllegalArgumentException(oclDocURI.toString() + " doesn't contain an OCL Model"); |
| } |
| } |
| |
| private TypedModel leftTypedModel = QVTbaseFactory.eINSTANCE.createTypedModel(); |
| private TypedModel rightTypedModel = QVTbaseFactory.eINSTANCE.createTypedModel(); |
| |
| public Function<Model, CoreModel> oclModelToImperativeModel() { |
| return oclModel -> { |
| |
| CoreModel iModel = QVTcoreFactory.eINSTANCE.createCoreModel(); |
| |
| List<Operation> allAstOps = getAllContents().apply(oclModel) |
| .filter(isAstOp()) |
| .map(Operation.class::cast).collect(Collectors.toList()); |
| List<ShadowExp> shadowExps = allAstOps.stream() |
| .flatMap(getAllContents()) |
| .filter(ShadowExp.class::isInstance) |
| .map(ShadowExp.class::cast).collect(Collectors.toList()); |
| |
| iModel.setExternalURI(oclModel.getExternalURI().replace(".ocl", ".qvtm")); // When the externalURI is set, also is its name |
| iModel.getOwnedImports().addAll(oclModel.getOwnedImports().stream() |
| .map(importToImport()) |
| .collect(Collectors.toList())); |
| |
| Package pPackage = PivotFactory.eINSTANCE.createPackage(); |
| pPackage.setName(""); |
| iModel.getOwnedPackages().add(pPackage); |
| |
| Transformation pTx = QVTbaseFactory.eINSTANCE.createTransformation(); |
| pTx.setName(iModel.getName().replace('.', '_')); // FIXME . as part of the name is causing issues in the CG); |
| pPackage.getOwnedClasses().add(pTx); |
| |
| List<Package> importedPackges = new ArrayList<Package>(); |
| for (Namespace ns : iModel.getOwnedImports().stream() |
| .map(x -> x.getImportedNamespace()) |
| .collect(Collectors.toList())) { |
| if (ns instanceof Model) { |
| importedPackges.addAll(((Model)ns).getOwnedPackages()); |
| } else if (ns instanceof Package) { |
| importedPackges.add((Package) ns); |
| } else { |
| logger.warning("imported namespace not recognised: " + ns.getName()); |
| } |
| } |
| |
| leftTypedModel.setName(LEFT_MODEL_TYPE_NAME); |
| leftTypedModel.getUsedPackage().add( |
| importedPackges.stream() |
| .filter(p -> p.getName().equals(getExpressionContextType().apply(shadowExps.get(0)).getOwningPackage().getName())) |
| .findFirst().get()); |
| pTx.getModelParameter().add(leftTypedModel); |
| |
| rightTypedModel.setName(RIGHT_MODEL_TYPE_NAME); |
| rightTypedModel.getUsedPackage().add( |
| importedPackges.stream() |
| .filter(p -> p.getName().equals(shadowExps.get(0).getType().getOwningPackage().getName())) |
| .findFirst().get()); |
| pTx.getModelParameter().add(rightTypedModel); |
| |
| pTx.getRule().addAll(shadowExps.stream() |
| .filter(shadowExpToCreationMappingGuard()) |
| .map(shadowExpToCreationMapping()) |
| .collect(Collectors.toList())); |
| return iModel; |
| }; |
| } |
| |
| public Function<@NonNull Import, @NonNull Import> importToImport(){ |
| return oclImport -> { |
| Import pImport = PivotFactory.eINSTANCE.createImport(); |
| pImport.setName(oclImport.getName()); |
| pImport.setImportedNamespace(oclImport.getImportedNamespace()); |
| return pImport; |
| }; |
| } |
| |
| private Predicate<@NonNull ShadowExp> shadowExpToCreationMappingGuard() { |
| return shadowExp -> { |
| return ! getAllContainers().apply(shadowExp) |
| .anyMatch(ShadowExp.class::isInstance); |
| }; |
| } |
| |
| public Function<@NonNull ShadowExp, @NonNull Mapping> shadowExpToCreationMapping() { |
| return shadowExp -> { |
| Mapping mapping = QVTcoreFactory.eINSTANCE.createMapping(); |
| mapping.setName(getCreationMappingName().apply(shadowExp)); |
| |
| CoreDomain leftDomain = createCreationMapping_LeftDomain(shadowExp); |
| CoreDomain rightDomain = createCreationMapping_RightDomain(shadowExp); |
| mapping.getDomain().add(leftDomain); |
| mapping.getDomain().add(rightDomain); |
| |
| Variable leftVar = leftDomain.getGuardPattern().getVariable().get(0); |
| Variable rightVar = rightDomain.getBottomPattern().getVariable().get(0); |
| |
| GuardPattern guardPattern = QVTcoreBaseFactory.eINSTANCE.createGuardPattern(); |
| BottomPattern bottomPattern = QVTcoreBaseFactory.eINSTANCE.createBottomPattern(); |
| mapping.setGuardPattern(guardPattern); |
| mapping.setBottomPattern(bottomPattern); |
| |
| PropertyAssignment pAssignment = QVTcoreBaseFactory.eINSTANCE.createPropertyAssignment(); |
| VariableExp value = PivotFactory.eINSTANCE.createVariableExp(); |
| value.setReferredVariable(rightVar); |
| value.setType(value.getReferredVariable().getType()); |
| |
| VariableExp slotExpression = PivotFactory.eINSTANCE.createVariableExp(); |
| slotExpression.setReferredVariable(leftVar); |
| slotExpression.setType(slotExpression.getReferredVariable().getType()); |
| |
| pAssignment.setValue(value); |
| pAssignment.setSlotExpression(slotExpression); |
| pAssignment.setTargetProperty(getTraceabilityProperty(slotExpression.getType())); |
| |
| bottomPattern.getAssignment().add(pAssignment); |
| updateGuardPattern(shadowExp, guardPattern, leftVar); |
| bottomPattern.getAssignment().addAll(shadowExp.getOwnedParts().stream(). |
| map(shadowPartToPropertyAssignment(leftVar, rightVar)). |
| collect(Collectors.toList())); |
| |
| return mapping; |
| }; |
| } |
| |
| |
| public Function<@NonNull ShadowPart, @NonNull PropertyAssignment> shadowPartToPropertyAssignment(Variable leftVar, Variable rightVar) { |
| return shadowPart -> { |
| VariableExp slotExpression = PivotFactory.eINSTANCE.createVariableExp(); |
| slotExpression.setReferredVariable(rightVar); |
| slotExpression.setType(slotExpression.getReferredVariable().getType()); |
| |
| PropertyAssignment pAssignment = QVTcoreBaseFactory.eINSTANCE.createPropertyAssignment(); |
| pAssignment.setTargetProperty(shadowPart.getReferredProperty()); |
| pAssignment.setValue(createPropertyAssignmentValue(shadowPart, leftVar)); |
| pAssignment.setSlotExpression(slotExpression); |
| return pAssignment; |
| }; |
| } |
| |
| |
| private CoreDomain createCreationMapping_LeftDomain(ShadowExp shadowExp) { |
| |
| Class contextType = getExpressionContextType().apply(shadowExp); |
| CoreDomain domain = QVTcoreBaseFactory.eINSTANCE.createCoreDomain(); |
| domain.setTypedModel(leftTypedModel); |
| domain.setIsCheckable(true); |
| |
| GuardPattern guardPattern = QVTcoreBaseFactory.eINSTANCE.createGuardPattern(); |
| BottomPattern bottomPattern = QVTcoreBaseFactory.eINSTANCE.createBottomPattern(); |
| domain.setGuardPattern(guardPattern); |
| domain.setBottomPattern(bottomPattern); |
| |
| Variable variable = PivotFactory.eINSTANCE.createVariable(); |
| variable.setName(firstToLowerCase().apply(contextType.getName())); |
| variable.setType(contextType); |
| |
| guardPattern.getVariable().add(variable); |
| |
| return domain; |
| } |
| |
| private CoreDomain createCreationMapping_RightDomain(ShadowExp shadowExp) { |
| |
| Class constructedType = shadowExp.getType(); |
| CoreDomain domain = QVTcoreBaseFactory.eINSTANCE.createCoreDomain(); |
| domain.setTypedModel(rightTypedModel); |
| domain.setIsEnforceable(true); |
| |
| GuardPattern guardPattern = QVTcoreBaseFactory.eINSTANCE.createGuardPattern(); |
| BottomPattern bottomPattern = QVTcoreBaseFactory.eINSTANCE.createBottomPattern(); |
| domain.setGuardPattern(guardPattern); |
| domain.setBottomPattern(bottomPattern); |
| |
| RealizedVariable variable = QVTcoreBaseFactory.eINSTANCE.createRealizedVariable(); |
| variable.setName(firstToLowerCase().apply(constructedType.getName())); |
| variable.setType(constructedType); |
| |
| bottomPattern.getRealizedVariable().add(variable); |
| return domain; |
| } |
| |
| private OCLExpression createPropertyAssignmentValue(ShadowPart shadowPart, Variable leftVar) { |
| |
| // FIXME what happens with synthetised types ???? |
| OCLExpression initExp = shadowPart.getOwnedInit(); |
| OCLExpression newInitExp = EcoreUtil.copy(initExp); |
| //We need to replace the OCL refered "self" varible by the QVTi domain "leftVar" and ast op calls |
| return doReplacements(newInitExp, leftVar); |
| } |
| |
| private OCLExpression doReplacements(OCLExpression oclExp, Variable leftVar) { |
| |
| List<OCLExpression> result = new ArrayList<OCLExpression>();// Simple work aroound to the forEach constraint ; |
| result.add(oclExp); |
| getAllContentsIncludingSelf().apply(oclExp).forEach(x -> { |
| if (isSelfVarExp().test(x)) { |
| ((VariableExp)x).setReferredVariable(leftVar); |
| } else if(isAstOpCallExp().test(x)) { |
| OperationCallExp exp = (OperationCallExp) x; |
| PropertyCallExp astPropCallExp = PivotFactory.eINSTANCE.createPropertyCallExp(); |
| astPropCallExp.setOwnedSource(exp.getOwnedSource()); |
| astPropCallExp.setReferredProperty(getTraceabilityProperty(astPropCallExp.getOwnedSource().getType())); |
| astPropCallExp.setType(astPropCallExp.getReferredProperty().getType()); |
| // Copying remaining changeable OpCallExp properties (excepting ownedArguments and referredOperation) |
| astPropCallExp.setTypeValue(exp.getTypeValue()); |
| astPropCallExp.getOwnedComments().addAll(exp.getOwnedComments()); |
| astPropCallExp.getOwnedExtensions().addAll(exp.getOwnedExtensions()); |
| astPropCallExp.setName(exp.getName()); |
| astPropCallExp.setIsSafe(exp.isIsSafe()); |
| astPropCallExp.setIsRequired(exp.isIsRequired()); |
| astPropCallExp.setIsImplicit(exp.isIsImplicit()); |
| astPropCallExp.setIsPre(exp.isIsPre()); |
| // end of copy |
| |
| Type castType = exp.getType(); |
| OperationCallExp asTypeOpCallExp = PivotFactory.eINSTANCE.createOperationCallExp(); |
| asTypeOpCallExp.setOwnedSource(astPropCallExp); |
| asTypeOpCallExp.setReferredOperation(getOclAnyOclAsTypeOp()); |
| asTypeOpCallExp.setType(castType); |
| asTypeOpCallExp.setIsSafe(astPropCallExp.isIsSafe()); |
| |
| TypeExp argTypeExp = PivotFactory.eINSTANCE.createTypeExp(); |
| argTypeExp.setReferredType(castType); |
| argTypeExp.setType(getOclMetaClass()); |
| |
| asTypeOpCallExp.getOwnedArguments().add(argTypeExp); |
| |
| if (result.contains(exp)) { // if exp is the initial oclExp, the new asTypeOpCallExp will be the new result |
| result.remove(exp); |
| result.add(asTypeOpCallExp); |
| } else { |
| EcoreUtil.replace(exp,asTypeOpCallExp); |
| } |
| EcoreUtil.delete(exp); |
| } |
| }); |
| return result.get(0); |
| } |
| |
| /** |
| * 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 |
| */ |
| private void updateGuardPattern(ShadowExp shadowExp, GuardPattern guardPattern, Variable leftVar) { |
| |
| EObject container = shadowExp.eContainer(); |
| |
| if (container instanceof IfExp) { |
| IfExp ifExp = (IfExp) container; |
| OCLExpression condition = ifExp.getOwnedCondition(); |
| List<OCLExpression> guardPredicates = new ArrayList<OCLExpression>(); |
| if (ifExp.getOwnedThen() == shadowExp) { |
| guardPredicates.add(EcoreUtil.copy(condition)); |
| } else { // it's the else |
| guardPredicates.add(createNegatedExpression(condition)); |
| } |
| |
| container = container.eContainer(); |
| // FIXME create a cached operation to improve performance |
| while (container instanceof IfExp) { |
| ifExp = (IfExp) container; |
| guardPredicates.add(createNegatedExpression(ifExp.getOwnedCondition())); |
| container = container.eContainer(); |
| } |
| |
| // We need to replace the OCL refered "self" varible by the QVTi domain "leftVar" |
| Collections.reverse(guardPredicates); |
| for (OCLExpression guardPredicate : guardPredicates ) { |
| org.eclipse.qvtd.pivot.qvtbase.Predicate predicate = QVTbaseFactory.eINSTANCE.createPredicate(); |
| predicate.setConditionExpression(doReplacements(guardPredicate, leftVar)); |
| guardPattern.getPredicate().add(predicate); |
| } |
| } |
| } |
| |
| private OCLExpression createNegatedExpression(OCLExpression oclExp){ |
| // We don't want to create a 'not not conditionExp' |
| if (isBooleanNotOpCallExp().test(oclExp)) { |
| OperationCallExp notOpCallExp = (OperationCallExp) oclExp; |
| return EcoreUtil.copy(notOpCallExp.getOwnedSource()); |
| } else { |
| OperationCallExp notOpCallExp = PivotFactory.eINSTANCE.createOperationCallExp(); |
| notOpCallExp.setName("not"); |
| notOpCallExp.setReferredOperation(getBooleanNotOp()); |
| notOpCallExp.setType(getBooleanPrimitiveType()); |
| notOpCallExp.setOwnedSource(EcoreUtil.copy(oclExp)); |
| return notOpCallExp; |
| } |
| } |
| |
| private Predicate<EObject> isSelfVarExp() { |
| return element -> { |
| return element instanceof VariableExp && |
| "self".equals(((VariableExp)element).getReferredVariable().getName()); |
| }; |
| } |
| |
| private Predicate<EObject> isAstOpCallExp() { |
| return element -> { |
| return element instanceof OperationCallExp && |
| isAstOp().test(((OperationCallExp)element).getReferredOperation()); |
| }; |
| } |
| |
| private Predicate<EObject> isAstOp() { |
| return element -> { |
| return element instanceof Operation && |
| "ast".equals(((Operation)element).getName()); |
| }; |
| } |
| |
| /*private List<OperationCallExp> getAstCalls(ShadowPart shadowPart) { |
| return getAllContentsIncludingSelf().apply(shadowPart.getOwnedInit()) |
| .filter(isAstOpCallExp()) |
| .map(OperationCallExp.class::cast) |
| .collect(Collectors.toList()); |
| }*/ |
| |
| private @NonNull Property getTraceabilityProperty(Type type) { |
| Class aClass = type.isClass(); |
| assert(aClass != null); |
| Set<Class> allClasses = getSuperClasses().apply(aClass); |
| allClasses.add(aClass); |
| return allClasses.stream() |
| .flatMap(x -> x.getOwnedProperties().stream()) |
| .filter(x -> traceabilityPropName.equals(x.getName())) |
| .findFirst().get(); |
| } |
| |
| private @NonNull Operation getOclAnyEqualsOp() { |
| Class oclAny = envFact.getStandardLibrary().getOclAnyType(); |
| return envFact.getMetamodelManager().getPrimaryClass(oclAny).getOwnedOperations().stream() |
| .filter(x -> "=".equals(x.getName())) |
| .findFirst().get(); |
| } |
| |
| private @NonNull Class getOclMetaClass() { |
| return envFact.getStandardLibrary().getClassType(); |
| } |
| |
| private @NonNull Operation getOclAnyOclAsTypeOp() { |
| Class oclAny = envFact.getStandardLibrary().getOclAnyType(); |
| return envFact.getMetamodelManager().getPrimaryClass(oclAny).getOwnedOperations().stream() |
| .filter(x -> "oclAsType".equals(x.getName())) |
| .findFirst().get(); |
| } |
| |
| private @NonNull Class getBooleanPrimitiveType() { |
| return envFact.getStandardLibrary().getBooleanType(); |
| |
| } |
| |
| private @NonNull Operation getBooleanNotOp() { |
| Class boolType = envFact.getStandardLibrary().getBooleanType(); |
| return envFact.getMetamodelManager().getPrimaryClass(boolType).getOwnedOperations().stream() |
| .filter(x -> "not".equals(x.getName())) |
| .findFirst().get(); |
| } |
| |
| private Predicate<OCLExpression> isBooleanNotOpCallExp() { |
| return exp -> { |
| return exp instanceof OperationCallExp && |
| ((OperationCallExp)exp).getReferredOperation() == getBooleanNotOp(); |
| }; |
| } |
| |
| } |