In this post I show how to (ab)use PlantUML to visualize Xtext models. Warning: This is about diagrams, not about UML.
PlantUML Intro
PlantUML is an “Open Source tool [...] to draw UML using a simple and intuitive language”.
Let me just draw an example class diagram to demonstrate how it feels:
The following PlantUML specification
@startuml abstract class Superclass << general >>; abstract AbstractCreator{ {abstract} create() : Superclass } Superclass <|-- Subclass note left of Superclass : Instantiation not possible ConcreteCreator-up-|> AbstractCreator ConcreteCreator : create() : Superclass ConcreteCreator .> Subclass @enduml
results in this diagram:
PlantUML also provides an Eclipse plugin which dynamically visualizes the “current active diagram, i.e. the diagram where the text cursor is located”. Better than that, this plugin provides an extension which lets you contribute a diagramTextProvider for a given editor. This means you can specify custom visualizations (limited by the PlantUML’s “UML”-capabilities) for any editor input. And the best of it: It works dynamically, i.e. while typing.
Create Xtext Project
Create a new Xtext Project (accept all defaults). Adjust the grammar to make it look like this
// dont adjust grammar header Model: greetings+=Greeting*; Greeting: from=ID 'says' message=STRING 'to' to=ID;
and run the GenerateMyDsl.mwe2-worklow.
Create diagramTextProvider
Navigate to the ui project of your language. Add a dependency to the PlantUML-plugin to the MANIFEST.MF:
Require-Bundle: ...,net.sourceforge.plantuml.eclipse;
In the plugin.xml, add the following extension point:
<extension point="net.sourceforge.plantuml.eclipse.diagramTextProvider"> <diagramTextProvider fileExtensions="mydsl" providerClass="org.xtext.example.mydsl.ui.plantuml.MyDiagramTextProvider"> </diagramTextProvider> </extension>
Implement MyDiagramTextProvider as follows:
class MyDiagramTextProvider extends AbstractDiagramTextProvider { def DomainmodelDiagramTextProvider() { fileExtensions = "mydsl"; editorType = typeof(XtextEditor) } override String getDiagramText(IEditorPart editorPart, IEditorInput editorInput) { // Retrieve the "semantic" EMF from XtextEditor val document = (editorPart as XtextEditor).getDocumentProvider().getDocument(editorInput) as XtextDocument; val Model model = document.readOnly[ return contents.head as Model ] // Collect names specified in textual model into Set val allNames = new HashSet model.greetings.forEach[if(from!=null) allNames.add(from); if(to!=null) allNames.add(to)] // draw some actors and labelled arrows ''' actor «FOR n:allNames»:«n»: as «n» «ENDFOR» «FOR it:model.greetings.filter[from!=null && to!=null && message!=null]» «from»->«to»:«message» «ENDFOR» ''' } }
Some notes on this implementation:
- Apparently, this is an Xtend class.
- I did not implement DiagramTextProvider directly. Instead, I extended AbstractDiagramTextProvider which comes with a lot of boilerplate code.
Try it
Run the Xtext/PlantUML plugins. It looks like (note: the editor is dirty … it really works dynamically):
Comments