Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Papyrus Software Designer/Model Code Synchronization"

(UML and C)
Line 140: Line 140:
  
 
== UML and C ==
 
== UML and C ==
 +
 +
To prepare for a future prototype and feature development for synchronizing UML models with C source code, the change notification API of Eclipse CDT (https://eclipse.org/cdt/) is evaluated. Based on this evaluation, it seems possible to develop a synchronization that uses the same architecture as the UML-Java synchronization and even reuse some code if the mapping definitions are not too different.
  
 
* Element change istener:
 
* Element change istener:
Line 152: Line 154:
 
** C/C++ problem (compiler) vs. Semantic error (analysis)
 
** C/C++ problem (compiler) vs. Semantic error (analysis)
 
** Analysis executed on editor in focus
 
** Analysis executed on editor in focus
 +
 +
The following sections provide some examples of change notifications provided by the CDT API with Xtend codes at the beginning to show how they were recorded and how the API can be used for registering these listeners.
  
 
=== CDT element changed notifications: ===
 
=== CDT element changed notifications: ===

Revision as of 06:25, 15 December 2016

Model Code Synchronization

This page gathers ideas on the model-code synchronization component of Papyrus Software Designer, developed by IncQuery Labs and CEA. It will later become a finalized wiki page for users and developers. The synchronization component is based on change-based incremental transformation (code generation and reverse).

UML and Java

Events to handle (both UML side and Java side):

  • Addition
  • Deletion
  • Rename
  • Move
    • = Addition + Deletion?
  • Refactor [UML-side] such as “impact of changing any element E on other elements that reference ”, e.g.
    • Impact of a renaming performed on a class which types properties
    • Impact of moving some class, typing properties, to another package
  • Refactor [Java-side]
    • By using the JDT refactor command, referenced JDT elements will be updated, firing change events so no problem.
    • Without using JDT refactor command, we will have broken code to be ignored (e.g. attribute with nonexistent type). I don’t think JDT will fire a change event on the broken code.

It doesn’t matter if such events are not explicit, as long as we can express them (e.g. combination of other events) and update elements without ambiguity.

Mapping definition

Mapping root

The root of the mapping is selected by the user:

  • Java: packages are handled with source folders considered as roots that are not mapped themselves
  • UML: by default, the root object of the UML model (a Model EObject, e.g. RootElement) is already a Package that can be mapped to a root Java package, or an added prefix (e.g. com.mycompany) can be used to map the contents of the model to a sub package (e.g. com.mycompany.RootElement)

Note that the root of the mapping is best described as a Java package and a corresponding UML Package (potentially Model) element, where the namespace (container package qualified name) of the Java package is the prefix for the code generated from the UML Package.

  • Java root object <-sync-> UML root object

Packages

  • A Java package is mapped to a Package object in the UML model.
    • Java package (JP) <-> UML Package (UP)
  • The name attribute of the Package is the same as the name of the Java package.
    • JP.name <-> UP.name
  • The container of the Package object is the same as the Package object synchronised with the Java package that contains the mapped Java package
    • JP.package <-> UP.owner

Compilation units

  • Compilation units (CU) are classes, interfaces and enums, each defined in source files (*.java).
    • Java class <-> UML Class
    • Java interface <-> UML Interface
    • Java enum <-> UML Enumeration
  • The name attribute of the UML NamedElement is the same as the name of the Java Package.
    • CU.name <-> NamedElement.name
  • The container of the CU is the same as the Package object synchronised with the Java package that contains the mapped CU
    • CU.package <-> Element.owner
  • The visibility of the compilation unit (public, private, package, protected) is the same as the VisibilityKind set for the UML element.
    • Only public and package-private visibility is allowed in units contained by packages
    • Inner types can use all visibility kinds
    • CU.visibility <-> NamedElement.visibility
  • Strictfp modifier is available on classes, interfaces and non-abstract methods to restrict floating point calculations for portability.
    • Java.strictfp <-> PapyrusJava.Strictfp stereotype applied on element
TODO details
    • Extends (may reference external types)
    • Implements (may reference external types)
    • Generics (may reference external types)
    • Imports

Class

  • Java classes (JC) have fields and methods that are mapped to properties and operations in UML Classes
    • See details in later sections
  • Class modifiers are abstract and final
    • JC.abstract <-> Classifier.abstract
    • JC.final <-> RedefinableElement.leaf

Interface

  • Interfaces only have public methods without body (before Java 8)
  • From Java 8, interface methods can define default implementations and static methods as well
  • See details in later sections

Enumeration

  • Java enum literals are mapped to UML EnumerationLiterals
    • Java enum literal <-> UML EnumerationLiteral
  • Initial value of literals can be represented by the specification of EnumerationLiterals in UML
    • In case of enums with constructors and fields, the literals have initial values using assignment
    • literal.inital <-> EnumerationLiteral.specification
  • Java enums may have methods and fields similarly to a class
    • See details in later sections

Members

TODO details
    • Visibility (public, private, package, protected)
    • Modifiers (static)
    • Type (return type in case of methods)

Fields

  • Java fields are mapped to UML Properties
    • Java field <-> UML Property
  • The container of the Property is the UML Element that is mapped to the container of the Java field.
    • Field.container <-> Property.owner (Owner.ownedAttribute)
  • Initial value is provided by assignment in Java, while UML represents this as default value
    • Field.initial <-> Property.defaultValue

Note that while fields could be represented by full Associations owned by the container, it is not done that way.

  • TODO there is a "Create Association From Property" command, that transforms simple properties to association, it is not clear how this should be handled in synchronization

Methods

  • Constructor
    • Method.isConstructor <-> standard.Create stereotype applied on Operation
TODO details
  • Override
  • Generics (may reference external types)
  • Parameters (may reference external types)
    • Type (may reference external types)
    • Generics (may reference external types)
  • Throws declarations in Java are represented by raised exceptions of the UML Operation
    • Method.throws <-> BehavioralFeature.raisedExceptions
  • Body

Annotations

  • TODO handling Annotations are not yet defined in mapping and not supported by code generator or reverse engineering transformation.

Comments

  • Comments in Java files that are added to elements and not inside blocks are stored in UML Comment elements

Inner types

  • Inner types are owned by a Compilation Unit instead of a Package
  • Static modifier can be used on inner types in Java
    • Type.static <-> PapyrusJava.StaticClassifier stereotype applied on Classifier

UML and C

To prepare for a future prototype and feature development for synchronizing UML models with C source code, the change notification API of Eclipse CDT (https://eclipse.org/cdt/) is evaluated. Based on this evaluation, it seems possible to develop a synchronization that uses the same architecture as the UML-Java synchronization and even reuse some code if the mapping definitions are not too different.

  • Element change istener:
    • Only called on elements that had their content changed
    • POST_CHANGE events on save
    • POST_RECONCILE events during editing, without save
  • Other translation units are not reconciled after changes automatically
    • If they are open in an editor then reconciliation occurs when the editor is in focus (already in focus or focus changes to it)
  • Build errors are different from code analysis errors
    • C/C++ problem (compiler) vs. Semantic error (analysis)
    • Analysis executed on editor in focus

The following sections provide some examples of change notifications provided by the CDT API with Xtend codes at the beginning to show how they were recorded and how the API can be used for registering these listeners.

CDT element changed notifications:

CoreModel.^default.addElementChangedListener[ event | 
	event.processEvent
]
 
def processEvent(ElementChangedEvent event) {
	val typeString = switch(event.type) {
		case 1 : "POST_CHANGE"
		case 4 : "POST_RECONCILE"
		case 5 : "POST_SHIFT"
	}
 
	val eventString = '''
		>> Element changed event received: 
		```
		«typeString»
		«event»
		```
	'''
 
	print(eventString)
}

Open C project tree to first level after startup

POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CONTENT}]

Create folder *abc* inside source folder

POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | CONTENT | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | CONTENT | FINE GRAINED}
			abc CCONTAINER [+]: {}
			ResourceDelta(/test/src/abc)[+]
		ResourceDelta(/test/src)[*]]

File level modifications

  • Open existing file *test.cpp*
POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			test.cpp WORKING_UNIT [+]: {}]

When AST is ready:

POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=test.cpp WORKING_UNIT [+]: {}]
  • Close unmodified file *test.cpp*
POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			test.cpp WORKING_UNIT [-]: {}]
  • Copy-paste file *test.cpp* with name *test2.cpp*
POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | CONTENT | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			test2.cpp TRANSLATION_UNIT [+]: {}
		ResourceDelta(/test/src)[*]]
  • Save modified file *test2.cpp*
POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | CONTENT | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			test2.cpp TRANSLATION_UNIT [*]: {CONTENT}
		ResourceDelta(/test/src)[*]]
POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=test2.cpp WORKING_UNIT [?]: {FINE GRAINED}]
  • Delete closed file *test2.cpp*
POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | CONTENT | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			test2.cpp TRANSLATION_UNIT [-]: {}
		ResourceDelta(/test/src)[*]]
  • Restore deleted file *test2.cpp*
POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | CONTENT | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			test2.cpp TRANSLATION_UNIT [+]: {}
		ResourceDelta(/test/src)[*]]
  • Create class *Test2* with namespace *a::b* in folder *abc*

Both header and source file is created, content added and working units closed in single transaction:

POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			abc CCONTAINER [*]: {CHILDREN | FINE GRAINED}
				Test2.h TRANSLATION_UNIT [*]: {CHILDREN | FINE GRAINED}
					ABC_TEST2_H_ C_MACRO [+]: {}
					a C_NAMESPACE [+]: {}
				Test2.cpp TRANSLATION_UNIT [*]: {CHILDREN | FINE GRAINED}
					Test2.h C_INCLUDE [+]: {}
					a C_NAMESPACE [+]: {}
				Test2.h WORKING_UNIT [-]: {}
				Test2.cpp WORKING_UNIT [-]: {}]

Translation units added (although already modified above, maybe from different thread?):

POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | CONTENT | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | CONTENT | FINE GRAINED}
			abc CCONTAINER [*]: {CHILDREN | FINE GRAINED}
				Test2.cpp TRANSLATION_UNIT [+]: {}
				Test2.h TRANSLATION_UNIT [+]: {}
			ResourceDelta(/test/src/abc)[*]
		ResourceDelta(/test/src)[*]]

Folder content changed?

POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | CONTENT | FINE GRAINED}
		src SOURCE_ROOT [*]: {CONTENT}
			ResourceDelta(/test/src/abc)[*]
		ResourceDelta(/test/src)[*]]

Test2.cpp and Test2.h files are opened in editor

POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			abc CCONTAINER [*]: {CHILDREN | FINE GRAINED}
				Test2.cpp WORKING_UNIT [+]: {}]
POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			abc CCONTAINER [*]: {CHILDREN | FINE GRAINED}
				Test2.h WORKING_UNIT [+]: {}]

After AST is built:

POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=Test2.cpp WORKING_UNIT [+]: {}]
POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=Test2.h WORKING_UNIT [+]: {}]
  • Open editor *Test2.cpp* gets focus
POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=Test2.cpp WORKING_UNIT [?]: {FINE GRAINED}]
  • Rename file *test2.cpp* to *test3.cpp*
POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | CONTENT | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | FINE GRAINED}
			test2.cpp TRANSLATION_UNIT [-]: {}
			test3.cpp TRANSLATION_UNIT [+]: {}
		ResourceDelta(/test/src)[*]]
  • Move file between directories
POST_CHANGE
org.eclipse.cdt.core.model.ElementChangedEvent[source= CMODEL [*]: {CHILDREN | FINE GRAINED}
	test CPROJECT [*]: {CHILDREN | CONTENT | FINE GRAINED}
		src SOURCE_ROOT [*]: {CHILDREN | CONTENT | FINE GRAINED}
			abc CCONTAINER [*]: {CHILDREN | FINE GRAINED}
				test3.cpp TRANSLATION_UNIT [+]: {}
			test3.cpp TRANSLATION_UNIT [-]: {}
			ResourceDelta(/test/src/abc)[*]
		ResourceDelta(/test/src)[*]]

File content modifications

  • Move function to namespace
POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=test.cpp WORKING_UNIT [*]: {CHILDREN | FINE GRAINED}
	x C_NAMESPACE [*]: {CHILDREN}
		f C_FUNCTION [+]: {}
	f C_FUNCTION [-]: {}]
  • Add argument to function
POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=test.cpp WORKING_UNIT [*]: {CHILDREN | FINE GRAINED}
	x C_NAMESPACE [*]: {CHILDREN}
		f C_FUNCTION [+]: {}
		f C_FUNCTION [-]: {}]
    • Same change for:
      • Change argument type in function
      • Delete argument of function
  • Change type of variable
POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=test.cpp WORKING_UNIT [*]: {CHILDREN | FINE GRAINED}
	x C_VARIABLE [*]: {CONTENT}]
  • Add field to class
POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=Test2.h WORKING_UNIT [*]: {CHILDREN | FINE GRAINED}
	a C_NAMESPACE [*]: {CHILDREN}
		b C_NAMESPACE [*]: {CHILDREN}
			Test2 C_CLASS [*]: {CHILDREN}
				x C_FIELD [+]: {}]
  • Change type or visibility of field
POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=Test2.h WORKING_UNIT [*]: {CHILDREN | FINE GRAINED}
	a C_NAMESPACE [*]: {CHILDREN}
		b C_NAMESPACE [*]: {CHILDREN}
			Test2 C_CLASS [*]: {CHILDREN}
				x C_FIELD [*]: {CONTENT}]
  • Change initial value of field
POST_RECONCILE
org.eclipse.cdt.core.model.ElementChangedEvent[source=Test2.h WORKING_UNIT [?]: {FINE GRAINED}]

CDT Indexer notifications

Indexer state

Indexer state listener is called when indexer is set to busy or idle:

CCorePlugin.indexManager.addIndexerStateListener[ event |
	event.processIndexerStateEvent
]
 
def processIndexerStateEvent(IIndexerStateEvent event) {
	println('''
		>> Index state event:
		```
		Indexer is now «IF event.indexerIsIdle»IDLE«ELSE»BUSY«ENDIF»
		```
	''')
}

This information can be used for scheduling, e.g. fire activations once the indexer returns to idle state. Ensures that reading from index returns stable results.

Index change

Index change listener is called when the index changes:

CCorePlugin.indexManager.addIndexChangeListener[ event |
	event.processIndexChangeEvent
]
 
def processIndexChangeEvent(IIndexChangeEvent event) {
	println('''
		>> Index change event:
		```
		Affected project: «event.affectedProject»
		Files cleared «event.filesCleared»
		Files written «event.filesWritten»
		«IF event.cleared»Index cleared«ENDIF»
		«IF event.hasNewFile»Index has new file«ENDIF»
		«IF event.reloaded»Index reloaded«ENDIF»
		```
	''')
}

Indexer is only updated on save or other file level changes. Althought change events include the affected project and both the cleared and written files are arrays, the event arrives multiple times for the same project in a single processing (busy) period.

Deleting multiple files results in a single update.

Developer resources

UML-Java synchronization

  • Gerrit change: https://git.eclipse.org/r/#/c/85611/
  • Java plugins path: languages/java/sync
  • User interface plugin:
    • org.eclipse.papyrus.designer.languages.java.sync.ui
      • Contains the entry point to setting up the synchronization from the user interface
      • To start exploring, a good place would be org.eclipse.papyrus.designer.languages.java.sync.ui.SynchronisationModelHandler.startTransformation(IProject, Model)
      • Synchronizations handled be the UI are managed by org.eclipse.papyrus.designer.languages.java.sync.ui.manager.RunningSynchronizationManager
  • Java-to-UML incremental transformation:
    • org.eclipse.papyrus.designer.languages.java.sync.reverse
      • Contains the code that uses VIATRA Transformation EVM-JDT integration to update the UML model
      • Transformation entry point org.eclipse.papyrus.designer.languages.java.sync.reverse.JDTUMLTransformation
      • High level rules are in the org.eclipse.papyrus.designer.languages.java.sync.reverse.rules package
        • At this point only TransactionalCompilationUnitRule and PackageRule are used
      • AST visitor for processing Java source code: org.eclipse.papyrus.designer.languages.java.sync.reverse.rules.visitors.TypeVisitor
    • org.eclipse.papyrus.designer.languages.java.sync.umlmanipulator
      • Contains the interface and implementation for manipulating the UML model
      • See org.eclipse.papyrus.designer.languages.java.sync.umlmanipulator.UMLModelAccess
  • UML-to-Java incremental transformation:
    • org.eclipse.papyrus.designer.languages.java.sync.codegen
      • Contains the code that uses VIATRA Transformation to update the Java source code
      • Transformation entry point org.eclipse.papyrus.designer.languages.java.sync.codegen.UMLToJavaTransformation
      • Transformation rules are in the org.eclipse.papyrus.designer.languages.java.sync.codegen.rules package
    • org.eclipse.papyrus.designer.languages.java.sync.jdtmanipulator
      • Contains the interface and implementation for manipulating the Java source code through the JDT API
      • See org.eclipse.papyrus.designer.languages.java.sync.jdtmanipulator.IJDTManipulator

Back to the top