Rpy Interoperability Developer
This documentation talks about the import of models created with the software
IBM Rational Rhapsody into Papyrus.
IBM Rational Rhapsody is developed by the IBM company. In this documentation, we reference this software as
Rhapsody.
Which Rhapsody version models are supported?
The migration tool has been developed with IBM Rhapsody 8.0.3. Nevertheless, it should work with previous and next versions.
Where are located the Papyrus Intereoperability Rpy plugins?
All Papyrus Rpy user plugins are stored in the Papyrus git
org.eclipse.papyrus-interoperability.git, into the subfolder
rpy.
-
org.eclipse.papyrus.interoperability.rpy.metamodel,
-
org.eclipse.papyrus.interoperability.rpy.metamodel.edit,
-
org.eclipse.papyrus.interoperability.rpy.metamodel.editor:
These 3 plugins provide the papyrus umlrpy metamodel based on Ecore. They describe the internal Rhapsody metamodel.
The models created with this metamodel used the file extension
*.umlrpy. The QVTo transformations are applied on these files to create the Papyrus UML models.
Rhapsody and Papyrus are representing differently similar concepts. The Rhapsody to Papyrus import process is implemented as a set of mapping rules between those two representations.
In order to express the mapping rules, a description of Rhapsody representation of UML and graphical concepts has been implemented with a so-called “ecore metamodel�?.
This metamodel has been built thanks to an analysis of two complementary public informations:
- the public java API providing a first list of the concepts and their inheritance relationship. The documentation of this API located in Doc/java_api/index.html of Rhapsody install folder. The public java API is located in 'Share/JavaAPI/rhapsody.jar'.
- the list of 150+ examples provided in the Sample directory. Those examples provided a good overview of all the concepts involved in Rhapsody models and how they are serialized in textual files. In order to accelerate the development, this analysis process has been automated in “one shot�? script which is not part of the delivered software.
However, an automated update process is provided as a “developer feature�? : when a user provides a new Rhapsody model containing concepts which had not been encountered in the analyzed examples, the metamodel update with those new concepts can be automated. This features is made to accelerate the implementation of a new mapping rule to an equivalent Papyrus concept.
To update the metamodel, you can use the plugin
org.eclipse.papyrus.interoperability.rpy.toolsmiths.api.discovery
- we also update this metamodel to be ease the development of the QVto transformation. That's why it is possible you met a property or an inheritance which doesn't exist in pure Rhapsody. In this case this property comes from a hand change done by a developer to ease the QVTo transformation writting process. It is not embarrassing to use a wrong EMF rhapsody metamodel to go from Rhapsody to Papyrus, because in this case, it will be more permissive. Nevertheless, it will be a problem to use it later to transform a Papyrus model into a Rhapsody model.
- It seems that the object
IModelElement is the common ancestor to Rhapsody semantic element. Be careful with these objects:
- in Rhapsody the
Diagram concept is a semantic element (and not a graphical element). Diagram can be stereotyped
-
ISysMLPort is not a stereotype but a metamodel element for Rhapsody.
Papyrus Interoperability Rpy plugins
-
org.eclipse.papyrus.interoperability.rpy.metamodel,
-
org.eclipse.papyrus.interoperability.rpy.metamodel.edit,
-
org.eclipse.papyrus.interoperability.rpy.metamodel.editor:
- Please, see description in previous part.
-
org.eclipse.papyrus.interoperability.rpy.blackboxes:
- This plugin provides some useful blackboxes (java code). They are called by the QVTo transformations.
- These blackboxes are not embedded in the same plugin than the QVTo transformations to avoid compilation errors at the developer level and ease the development itself.
-
org.eclipse.papyrus.interoperability.rpy.geometry:
- This plugin provides objects to represent Rhapsody graphical elements and manipulate them easily to get their size and their location.
-
org.eclipse.papyrus.interoperability.rpy.parser,
-
org.eclipse.papyrus.interoperability.rpy.parser.ui:
- These plugins are used to parse the Rhapsody files (
*.rpy and others)
-
org.eclipse.papyrus.interoperability.rpy.toolsmiths.api.discovery:
- It is a developer plugin
- This plugin is not delivered, but it is build (a pom.xml file is provided)
- This plugin allows to update Rpy EMF metamodel when we meet models inconsistent with the current one.
-
org.eclipse.papyrus.interoperability.rpy:
- This plugin uses the other plugins described previously. It provides the API to convert a
*.rpy into a Papyrus model (
*.uml,
*.notation,
*.di and
*.properties files).
In addition, there are the plugins
org.eclipse.papyrus.uml.m2m.qvto.common and
org.eclipse.papyrus.uml.m2m.qvto.common.blackboxes located on the git org.eclipse.papyrus.git in the folder
plugins/uml/m2m. These plugins group common code used by Rpy, RSA and Sysml14 interoperability tools.
JUnit tests are provided too. They use EMF-Compare to check that the imported models continues to be equals to the expected one.
How does the migration process work?
The migration process is done in 3 steps:
- the
*rpy file is parsed by the XText parser (plugin
org.eclipse.papyrus.interoperability.rhapsody.parser) and converted into a first simple model using a first EMF metamodel,
RpySyntax, provided by this plugin, then
- this first model is converted into a set of
*.umlrpy by the class
org.eclipse.papyrus.interoperability.rpy.importer.UMLRpyImporter. There is a
*.umlrpy file for the initial selected
*rpy file, and one other for each subpackage of the imported Rhaposdy model. At the end of this process, we transform these files into the Papyrus UML model.
- In the Rhapsody model, there are references to objects provided by Rhapsody Libraries (types, stereotypes, ...) . As we can't reuse them directly (for legal reason), we create "proxy object" during the creation of the
*.umlrpy file with an EAnnotation referencing the name of the Rhapsody file providing it. This information is used later in the process by the QVto transformation.
The QVTo files are stored in the plugin
org.eclipse.papyrus.interoperability.rpy.
Cast
Casting collection doing
Type seems change arbitrary the order of the elements. It is a pattern to avoid.
- Transformation file have a kind of constructor preceded by the transform keyword and contains a method main. They are registered with the extension point
org.eclipse.m2m.qvt.oml.runtime.qvtTransformation as transformation.
- Library file have a name preceded by the keyword library. They must be registered with the extension point
org.eclipse.m2m.qvt.oml.runtime.qvtTransformation, as transformation or as library.
- All transformations must be called in the same context. That's why there is a kind of 'master' transformation calling the others. It is not possible to call them separately, because calling them separately we won't be able to find the result of a previous mapping using
resolve/invresolve function.
- Here, this is the transformation
Rpy2PapyrusNotation which call the others following this order:
Rpy2PapyrusSemanticElements,
- then calls
SemanticInternationalization
- then calls
SysML11Profile,
- then calls
SysML11Diagrams which calls the qvto transformations for
InternalBlockDiagram,
BlockDefinitionDiagram and
ParametricDiagram,
- and, in addition, it completes the
*.properties file with the label of the diagram when they exist.
Inheritance
It seems possible, but not yet used.
- The development has been done with all required plugins in the workspace.
- In the first Eclipse instance, the plugin owning the QVTo files (org.eclipse.papyrus.migration.rhapsody) doesn't compile because QVTo builder isn't able to resolve dependencies when they are in the same workspace. Moreover blackboxes registered in a plugin.xml can't be resolved.
- We launch a 2nd Eclipse to write QVTo transformation with no compilation error
- We launch a third Eclipse to launch transformation and check the result
It is possible to use breakpoint to debug QVTo transformation, but in this case you must use the Operatinal QVT Interpreter provided as Debug tool of Eclipse.
QVTo Tricks
Here, we describe the main QVTo keyword to know:
mapping it allows to declare a mapping method between two objects. It can have parameter. The resulting object is created before the first line of the mapping. If you want avoid to create a new object, you can look it for the object to return using the init{your code} block. Calling a given mapping for a given object will create a new object the first time and will return it other time. No new object will be created calling a given mapping several time with the same parameters.
init{your code} it allows to assign the result without creating an object. If at the end of this block, the result is null, the object will be created.
disjuncts it allows to declare a method dispatching an action to one of the others declared method.
when it allows to define a condition to allow the mapping (always used by a mapping called by a disjuncts mapping)
result keyword defining the result of a mapping
init to start a mapping, it allows to initializing the result without create an element
@ allows to define the file owning the object when the transformation has several output files.
- QVTo/OCL are not able to cast UML Element or Rpy Element in an other object inherited from an other metamodel implicitly. So to convert a
uml.Element into ecore.EObject you need to write the cast, otherwise it won't compile. So you can write : Element.oclAsType(EObject) or Element!
EObject to do that.
- To ease transformation writing, we advice to use
disjuncts each time it is possible to create common method. To our mind, the best common method will be declared as this example: umlrpy::IModelElement::commonMethod:uml:Element disjuncts. This pattern ease the call to resolve/invresolve function to retrieve an object previously created ignoring the real mapping method used. As an object is created only one time, you can also replace the resolve/invesolve functions, calling the mapping directly.
resolveOne/invresolveOne could seems interesting, but often an object can be used as entry of several mapping. For example, we use IObjectLink to create the UML Connector and its two ConnectorEnds.
- How to find easily the result of previously executed transformation ?
- The most common usecase is to set the element (EObject) representing a View during the graphical transformation. The QVTo method called resolved required to know which mapping was used to create the semantic element. The best solution is to always call the same common method to do the mapping. 2 implementations are possible:
- create a common mapping from a given type in the source metamodel to the most generic type of the target metamodel. This mapping will call the good submapping using
disjunct and
when QVTo keyword. Use resolved/invresolved to find the good object.
- after a mapping call a dummy method used as map (key,value) to store the result of the mapping:
mapping EObject::StoreSourceAndResult(res : EObject) : EObject {
init { result := res }
}
- When do I apply stereotype ?
It could seem a good idea to apply them when required in the mapping method, but it is not possible, because
- the result of the mapping is not yet in stored in the resource, so we get a NPE (main reason),
- in some case stereotype properties reference UML object not yet mapped (or stereotype application of others objects),
- more complex to override when we want extends a transformation to manage others profiles.
That's why we make a second run to apply profiles and stereotypes on the objects
- How to get Stereotype Application to edit its feature ?
- the resource edited during the transformation is in an other resourceset than the object given as parameter of the transformation, so this code deosn't work:
Stereotype firstStereotype=...//(coming from a parameter of the transformation)
element.applyStereotype(firstStereotype);
element.setValue(firstStereotype,"propName", newValue);//doesn't work
so we must do :
Stereotype firstStereotype=...//(coming from a parameter of the transformation)
element.applyStereotype(firstStereotype);
Stereotype secondStereotype=element.getAppliedStereotype(firstStereotype.getQualifiedName())
element.setValue(secondStereotype,"propName", newValue);//works, first and secondStereotype are equals; their resource have the same URI, but 2 instance of the same resource is loaded
Of course, in case of static profile, we can create the EClass represented the UML Stereotype in a mapping method too.
General Tricks
- When the
*rpy didn't change, to test your code quicker, you can select the file
*.umlrpy directly to import it as Papyrus model.
Java code
The plugin
org.eclipse.papyrus.interoperability.common is a refactoring of the code provided for RSA migration tool. A part of the embedded code initially written for RSA migration tool is now embedded in this plugin used by Rpy migration tool, but not yet used.
The QVTo transformation is launched by the class org.eclipse.papyrus.interoperability.rpy.transformations.RpyImportTransformationLauncher.
The creation of the
*.umlrhapsody file is done by the class org.eclipse.papyrus.interoperability.rpy.importer.SelectedRpyFilesImporter.
Versioning
To ease future fixes for generated corrupted models, an
EMF EAnnotation is added during the transformation to the root of the UML model and to all created diagrams. This EAnnotation contains:
- the version of the Papyrus Rpy Migration Tool,
- the version of the Rhapsody software used to create the model, and
- the name of the Rhapsody model.
This work is done by the QVTo library RpyToPapyrusUtils, with the helper method createEAnnotationForVersioning.
Diagram Styling
As we want to get the same style in import models than in the Rhapsody source model, we need to add EAnnoation PapyrusCSSForceValue to override the styling done by CSS stylesheet. The method to create a such EAnnotation is provided by the QVTo library RpyToPapyrusDiagamCommon, by the helper createCSSForceValue.