diff options
author | Adolfo SBH | 2016-02-04 16:37:48 +0000 |
---|---|---|
committer | Adolfo SBH | 2016-02-08 10:40:27 +0000 |
commit | 05b75f593d43331ebd3f130efc240572ab6a049b (patch) | |
tree | 2dc19c6e565a6aa6539eec490eb98c927d406db3 | |
parent | 93db4af60f6ca0503416733c9036a1356fadc3eb (diff) | |
download | org.eclipse.qvtd-asanchez/ocl2qvtm.tar.gz org.eclipse.qvtd-asanchez/ocl2qvtm.tar.xz org.eclipse.qvtd-asanchez/ocl2qvtm.zip |
[ocl2qvtp] - Introducing OCL2QVTm componentasanchez/ocl2qvtm
2 files changed, 469 insertions, 1 deletions
diff --git a/plugins/org.eclipse.qvtd.cs2as.compiler/src/org/eclipse/qvtd/cs2as/compiler/internal/OCL2QVTiCompilerChain.java b/plugins/org.eclipse.qvtd.cs2as.compiler/src/org/eclipse/qvtd/cs2as/compiler/internal/OCL2QVTiCompilerChain.java index ab1218f36..167eeab39 100644 --- a/plugins/org.eclipse.qvtd.cs2as.compiler/src/org/eclipse/qvtd/cs2as/compiler/internal/OCL2QVTiCompilerChain.java +++ b/plugins/org.eclipse.qvtd.cs2as.compiler/src/org/eclipse/qvtd/cs2as/compiler/internal/OCL2QVTiCompilerChain.java @@ -47,7 +47,8 @@ public class OCL2QVTiCompilerChain extends AbstractCompilerChain { @Override public @NonNull Transformation compile(@NonNull String enforcedOutputName) throws IOException { - return qvtp2qvti(ocl2qvtp()); + return qvtp2qvti(qvtm2qvtp(ocl2qvtm(oclASUri))); + // return qvtp2qvti(ocl2qvtp()); } public @NonNull Transformation compile() throws IOException { @@ -77,6 +78,13 @@ public class OCL2QVTiCompilerChain extends AbstractCompilerChain { return pResource; } + protected Resource ocl2qvtm(URI oclURI) throws IOException { + OCL2QVTp ocl2qvtm = new OCL2QVTp(environmentFactory, traceabilityPropName); + Resource mResource = ocl2qvtm.run(environmentFactory.getMetamodelManager().getASResourceSet(), oclURI); + saveResource(mResource, QVTM_STEP); + return mResource; + } + private @NonNull String getTraceabilityPropertyName() { String tracePropName = getOption(QVTP_STEP, TRACE_PROPERTY_NAME_KEY); return tracePropName == null ? DEFAULT_TRACE_PROPERTY_NAME : tracePropName; diff --git a/plugins/org.eclipse.qvtd.cs2as.compiler/src/org/eclipse/qvtd/cs2as/compiler/internal/OCL2QVTm.java b/plugins/org.eclipse.qvtd.cs2as.compiler/src/org/eclipse/qvtd/cs2as/compiler/internal/OCL2QVTm.java new file mode 100644 index 000000000..fbd104e5c --- /dev/null +++ b/plugins/org.eclipse.qvtd.cs2as.compiler/src/org/eclipse/qvtd/cs2as/compiler/internal/OCL2QVTm.java @@ -0,0 +1,460 @@ +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(); + }; + } + +}
\ No newline at end of file |