View on GitHub

QVTom

A Modular Extension for QVTo

Download this project as a .zip file Download this project as a tar.gz file

QVTom

QVTom is a prototypical implementation of the module system for model transformation languages described in [1]. It replaces the default structure of a QVTo transformation with the definition of interfaces and interface implementations where implementations can be linked to interfaces with an export or import relationship.

The changes that have been made to the QVTo plugin [2] are described in this document.

Changes to the AST/CST

Changes to the meta models

Legend:

Element Representation
CST CST: ...
AST AST: ...
line number in QVTOParser.gi <n>
modeltype PCM uses 'http://sdq.ipd.uka.de/PalladioComponentModel/5.0';
modeltype PCM_ALLOC uses 'http://sdq.ipd.uka.de/PalladioComponentModel/Allocation/5.0';
modeltype PCM_REP uses 'http://sdq.ipd.uka.de/PalladioComponentModel/Repository/5.0';

compilation_environment "Commons";
CST: CompilationEnvironmentCS[uriCS = "Commons"] 
compilation_environment "EventChannelMiddlewareRegistry";
compilation_environment "EventDistribution";
compilation_environment "EventFilter";
...

interface ISink( 
    inout pcmAllocation : PCM_ALLOC,
    CST: InterfaceInOutParamCS[param = ParameterDeclarationCS[simpleNameCS = "pcmAllocation", typeSpecCS = PCM_ALLOC, directionKind = inout]]
    AST: InterfaceRestrictionParameter[param = VarParameter[parsed by original parser]]
    inout pcmSystem : PCM_SYS, 
    inout pcmRepository : PCM_REP,
    in middlewareRepository : PCM_REP;
    
    in PCM_ALLOC[Allocation]
      CST: InterfaceRestrictionParamCS[param = PCM_ALLOC, classes = {Allocation::TypeSpecCS}, packages = {}] <780>
      AST: InterfaceRestrictionParameter
)
  AST: InterfaceParamsCS[…] 
  AST: ModuleHeaderCS[pathNameCS="ISink", interfaceInOutParamsCS=..., interfaceRestrictionParamsCS=...] <581>
{
    mapping Sink_createSinkOperationProvidedRole(sinkComponent : pcm::repository::RepositoryComponent,
                                                 operationInterface : pcm::repository::OperationInterface) : pcm::repository::OperationProvidedRole;
    <830> - declarations: mapping_decl / helper_decl, Klassen: MappingRuleCS, MappingDeclarationCS (with setBlackbox(true));
}
AST: ModuleInterfaceCS[methods = ..., moduleHeader = ..., metamodels = ModelTypes aus Parametern]
CST: ModuleInterface

 
module Sink mexport ISink
AST: ModuleHeaderCS, ExportCS[pathNameCS = "ISink"] <581>
{
    mimport ISEFFRegistry;  
    mimport ISEFFUtil;
    mimport ICommons;
    mimport IOperationSignatureRegistry;
        
    mapping Sink_createSinkOperationProvidedRole(sinkComponent : pcm::repository::RepositoryComponent,
                                                 operationInterface : pcm::repository::OperationInterface) : pcm::repository::OperationProvidedRole {
            entityName := operationInterface.entityName+'OperationProvidedRole'+Commons_getUniqueElementNameSuffix();
            providingEntity_ProvidedRole := sinkComponent;
            providedInterface__OperationProvidedRole := operationInterface;
    }
    
    <851> - implementations of mapping/helper methods: mapping_def / entry_def / helper_simple_def / helper_compund_def, Klassen: MappingMethodCS, MappingQueryCS, ModulePropertyCS
}

Changes to the grammar QVTOParser.gi

%Globals
    /.  
    [...]
    import org.eclipse.m2m.internal.qvt.oml.cst.ModuleHeaderCS;
    import org.eclipse.m2m.internal.qvt.oml.cst.MappingMethodCS;
    import org.eclipse.m2m.internal.qvt.oml.cst.InterfaceInOutParamCS;
    import org.eclipse.m2m.internal.qvt.oml.cst.InterfaceParamsCS;
    import org.eclipse.m2m.internal.qvt.oml.cst.InterfaceRestrictionParamCS;
    ./
%End

%KeyWords
    [...]
    module
    interface
    mimport
    mexport
    scope
    compilation_environment
%End


%Rules
    [...]
    unit_element -> compilation_env
    
    compilation_env ::= compilation_environment uri ';'
    
    unit_element -> module_def
    unit_element -> interface_def
    
    module_def ::= module_h mexport exportList '{' importList declarationList '}' semicolonOpt
    module_h ::= module qualifiedNameCS

    exportList ::= qualifiedNameCS
    exportList ::= exportList ',' qualifiedNameCS
    
    fullImport ::= mimport qualifiedNameCS ';'
    importList ::= %empty
    importList ::= importList fullImport
    
    interface_def ::= interface_h '{' interfaceList '}' semicolonOpt
    interface_h ::= interface qualifiedNameCS interfaceParams

    interfaceParams ::= '(' interfaceInOutParamList ')'
    interfaceParams ::= '(' interfaceInOutParamList ';' interfaceRestrictionList ')'
    
    interfaceInOutParamList ::= interfaceInOutParamList ',' interfaceInOutParam
    interfaceInOutParamList ::= interfaceInOutParam

    interfaceRestrictionList ::= interfaceRestrictionList ',' interfaceRestrictionParam
    interfaceRestrictionList ::= interfaceRestrictionParam
    interfaceRestrictionParam ::= param_direction typespec '[' typespecList ']'
    interfaceRestrictionParam ::= param_direction typespec '[' typespecList ']' '{' typespecList '}'
        
    paramWithDirection ::= param_direction IDENTIFIER ':' typespec
        
    interfaceInOutParam ::= paramWithDirection
        
    typespecList ::= %empty
    typespecList ::= typespec
    typespecList ::= typespecList ',' typespec
    
    -- returns a MappingRuleCS
    interfaceElement -> mapping_decl
    -- returns a MappingQueryCS
    interfaceElement -> helper_decl
    
    interfaceList ::= interfaceList interfaceElement
    interfaceList ::= interfaceElement

    -- return a MappingMethodCS
    declarationItem -> mapping_def
    declarationItem -> entry_def
    -- return a MappingQueryCS->MappingMethodCS
    declarationItem -> helper_simple_def
    declarationItem -> helper_compound_def
    
    declarationItem -> _property

    declarationList ::= declarationList declarationItem
    declarationList ::= declarationItem
    
    -- changed code ---
    scoped_identifier ::= scoped_identifier2
    scoped_identifier2 ::= IDENTIFIER '@' IDENTIFIER
%End

The new metamodels

AST

AST metamodel

CST

CST metamodel

How to include new elements into the CST/AST

  1. Once: adapt "/org.eclipse.m2m.qvt.oml.cst.parser/cst/run-lpg.cmd" (LPG_HOME, LPG_EXE, PERL_EXE)
    • Warning: spaces in path names can lead to problems
  2. Augment CST and AST metamodels, generate Java code.
    • /org.eclipse.m2m.qvt.oml.cst.parser/model/QVTOperationalCST.ecore
    • /org.eclipse.m2m.qvt.oml/model/QVTOperational.ecore
  3. Add new keywords to "/org.eclipse.m2m.qvt.oml.cst.parser/cst/QVTOKWLexer.gi" (export and rule)
  4. In "/org.eclipse.m2m.qvt.oml.cst.parser/cst/QVTOParser.gi"
    • Import keywords (%KeyWords)
    • Add rules with reference to factory method in org.eclipse.m2m.internal.qvt.oml.cst.parser.AbstractQVTParser
    • If possible, call setOffsets appropriatelyIn der Regel wenn möglich auch setOffsets entsprechend aufrufen.
  5. Call "/org.eclipse.m2m.qvt.oml.cst.parser/cst/run-lpg.cmd"
  6. If top level elements have been created (such as ModuleImplementation/ModuleInterface/CompilationEnvironment), add them to org.eclipse.m2m.internal.qvt.oml.cst.parser.AbstractQVTParser.setupTopLevel(EList<CSTNode>)

Changes to the CST (/org.eclipse.m2m.qvt.oml.cst.parser/model/QVTOperationalCST.ecore)

Changes to the AST (/org.eclipse.m2m.qvt.oml/model/QVTOperational.ecore)

org.eclipse.m2m.internal.qvt.oml.compiler.QVTOCompiler

The entry point for the changes from QVTo to QVTom start in org.eclipse.m2m.internal.qvt.oml.compiler.QVTOCompiler in the method doCompileQVTom, which replaces doCompileQVTo. For the compilation of QVTom in multiple files the following stages are performed:

  1. Parse all files that are declared as part of the "Compilation Environment" in the file.
  2. Create ModuleInterfaces (createModuleInterfaceStubs) for every CST, i.e. start the visitor (QVTOperationalVisitorCS) for every CST (every file).
  3. Resolve all cross references between modules (crossReferenceModules). Copy all references to every module interface (the elements that are used to resolve calls to methods of foreign modules) between each two environments of CSTParseResults (of type QVTOperationalFileEnv).
  4. Compile (CST -> AST) every imported file.
  5. Perform a transformation of the resulting QVTom-ASTs to a QVTO-AST which can be interpreted/compiled with the default interpreter/compiler.

org.eclipse.m2m.internal.qvt.oml.qvtom2qvto.QVTom2QVToCSTransformation

The linking of method calls etc. is already performed during the compilation. For the transformation from QVTom to QVTo the following steps have to be performed:

  1. Copy all methods from the module interfaces and module implementations to a new OperationalTransformation.
  2. Copy in/out/inout parameters to the new transformation.
  3. Create properties from the modules in the new transformation.

Changes to org.eclipse.m2m.internal.qvt.oml.ast.parser.QvtOperationalVisitorCS

Was modified to be able to handle the new syntax elements.

lookupModelParameter(SimpleNameCS, DirectionKind, QvtOperationalEnv)

Allows the referencing of ModelParameters that are declared in the interface, i.e. the parsed parameters from the ModuleInterface which are InterfaceInOutParameters are made referenceable.

QvtOperationalVisitorCS.genOperationCallExp(...)

If no local mapping can be found, check if a fitting method can be found in an imported module. For this purpose traverse upwards in the environment hierarchy until a fitting environment (of type QvtOperationalFileEnv) has been found and check if one of the imported interfaces implements a fitting method.

visitMappingDeclarationCS(...)

Check the visibility of the context types of the mappings.

visitResolveInExpCS(...)

Allow the resolution of methods in the same ModuleImplementation or in imported interfaces.

New methods for modularity in QvtOperationalVisitorCS

registerModelTypes(...)

Helper method that is used by visitModuleInterfaceand visitModuleImplementation. Parses the used ModelTypes (visitModelTypeCS) and references them in the created Module (getUsedModelType().add(...)), adds them as types to the module (.getEClassifiers().add(...)) and register them in the environment.

visitModuleInterface(...)

Visitor method for ModuleInterfaces, comparable to the visitor methods for methods.

  1. Register ModelTypes in the environment.
  2. Register the ModelInterface in the environment.
  3. Visit and reference InOutParametersand RestrictionParameters.
  4. Visit all MappingDeclarations for methods and reference results.

visitInOutParamsCS(...)

Visitor methods for InOutParameters.

visitRestrictionParamsCS(...)

Visitor method for RestrictionParams. All types are resolved and checked for containment of the package that is to be restricted.

visitModuleImplementation(...)

Visitor method for ModelImplementations.

  1. Register ModelTypes in the environment
  2. Create ModelImplementation and register it in the enviroinment.
  3. Create a new module environment ("moduleEnv") which encapsulates the contained methods and represents the module.
    • This is necessary to allow the free distribution of the interfaces and implementations to multiple files.
    • Imported interfaces and variables for used meta models are saved in the interface.
  4. Create properties (createModuleImplementationProperties)
  5. Resolve exports (resolveExports), i.e. resolve and reference each interface that is declared as export.
  6. Resolve imports, i.e. resolve and reference each interface that is declared as import.
  7. Pass through all contained methods
    1. Register contained method in moduleEnv (cf. visitMappingModule), i.e. create empty methods that can be referenced.
    2. Visit methods (references to empty methods are resolvable).
    3. Check if visibility declarations are violated.
      • validateMethodMetaModelVisibility(ModuleImplementation, QvtOperationalEnv)
      • OCLRestrictedTypeVisitor(ModuleImplementation, QvtOperationalEnv)
    4. Set entry operation
    5. Export the methods to the interface, cf. exportMethodsToInterface(...)`
  8. Pass the errors from the module environment to the parent environment to make them visible in the IDE.

exportMethodsToInterface(...)

In our implementation the method bodies are copied into the interface after the methods are visited (i.e. they are not referenced). We chose this seemingly complicated implementation to allow our modifications to be as non-invasive as possible and to allow the visitor methods for the mappings to perform unchanged.

This method also checks the exported interfaces for complete implementation in the ModuleImplementation and reports errors if necessary.

Meta model visibility validation

The validation of the meta model visibility is realized in two new classes which are explained below. The entry point into the validation can be found in two places:

OCLRestrictedTypeVisitor

Is created with a TypeRestrictionSetor a ModuleImplementation. When passing a ModuleImplementation the appropriate .buildFromInterface method of the TypeRestrictionSet is called.

The visitor itself extends OCLAbstractVisitor. Every possible violation of the meta model visibility can be checked and annotated with an appropriate error or warning if necessary.

TypeRestrictionSet

Represents a set of types and packages that are accessible for a particular direction (DirectionKind) and possibly for a particular extent.

QVTo(m)-Environments

Environments are created but not mandatorily persited. They can be used solely for the QVTOperationalVisitorCS and discarded afterwards.

org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalEnv

org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalModuleEnv, org.eclipse.m2m.internal.qvt.oml.ast.env.QvtOperationalFileEnv

Import mechanism of QVTo and QVTom

It is already possible to connect multiple transformations by an import mechanism in QVTo. Details for this mechanism can be found in the QVT standard.

Workflow of the QVTo(m) parser and interpreter

Image after Erweitern eines Code-Editors unter Eclipse um neue Sprachkonzepte by Ivayla Partalina.

Plugins/Packages/Classes

Example for parsing and environments

Source code

modeltype ECORE uses 'http://www.eclipse.org/emf/2002/Ecore';
modeltype UML uses 'http://www.eclipse.org/uml2/4.0.0/UML';

interface I_A(
    in ecore:ECORE,
    out uml:UML
) {
    mapping EClass::EClass2Class() : Class;
}

module A mexport I_A {
    mapping EClass::EClass2Class() : Class {
        name := self.name;
    }
}

Partial dump of the resulting CompiledUnit

result = CompiledUnit [
  moduleEnvs = [
    QvtOperationalFileEnv [
      parent = null,
      myFile = ".../minimal.qvto",
      myModuleImplementations = [
        ModuleImplementation[
          name = "A",
          eOperations = [MappingOperation[name="EClass2Class"]]
        ]
      ],
      myModuleInterfaces = [
        ModuleInterface[
          name = "I_A",
          operations = [MappingOperation[name="EClass2Class"]]
        ]
      ]
    ]
  ]
]

See Also

References

  1. A. Rentschler, D. Werle, Q. Noorshams, L. Happe, R. Reussner. Designing Information Hiding Modularity for Model Transformation Languages. Proceedings of the 13th International Conference on Modularity (AOSD '14), Lugano, Switzerland, April 2014. ACM, New York, NY, USA. April 2014.
  2. Eclipse Modeling - MMT - Project QVTo

Contributors