This tool takes a PlantUML file and converts it into another form. It was developed to create a, if possible complete, textual description of the Application Profile from the PlantUML file that represents the class model of DCAT-AP.de.
This "model-driven design" approach reduces errors because changes to the model (in the PlantUML file) are automatically reflected in the documentation. This prevents, for example, a newly added property from appearing only in the documentation but not in the class model.
The repository comes with examples that include an overview (examples/dcat-ap-de.plantuml) for the relatively complex Application Profile DCAT-AP.de ([templates/overview.hbs](templates/overview. hbs)), the data view of all classes and properties (templates/classes.hbs
) and the change history (templates/changelog.hbs
).
The Application Profile documentation was created with this generator and ReSpec. The visualisations of the Application Profile were created from the same PlantUML file using "Rich PlantUML support for Visual Studio Code".
"PlantUML-To-Ontology" (p2o
) is a NodeJS command line tool that reads a PlantUML file, passes it to the templates and saves the result.
The following must be installed: git, NodeJS and NPM.
git clone https://github.com/GovDataOfficial/plantuml-to-ontology.git
cd plantuml-to-ontology
npm install
node p2o.js <plantumlfile> [<template>...] [-e <extension>] [-o <output-dir>]
At least the PlantUML file must be specified. Without further specification, all available templates will be used.
With -e
the file extension of the generated files can be specified. Default is .md
.
With -o
the directory can be specified in which the generated files are stored. Default is ./output/
. The output directory must already exist, it will not be created by the tool.
Examples
node p2o.js examples/dcat-ap-de.plantuml
node p2o.js examples/mini.plantuml overview -e html
node p2o.js examples/mini.plantuml analysis
The analysis template (templates/analyse.hbs
) lists the contents of the ontology in a sorted manner. This makes it easy to get an overview of the classes, attributes and predicates used.
node p2o.js examples/mini.plantuml json
This command writes the data model generated by the grammar into the file ontology.json
. If you start the tool without specifying the templates to be used, the JSON representation will not be generated. If you specify the templates to be used, you can add JSON
if necessary.
PlantUML PlantUML is a component that allows you to quickly write class diagrams. Diagrams are defined using a simple and intuitive language.
In order to make the PlantUML class diagrams usable for further use, a Peggy grammar was written that can recognise a subset of the PlantUML syntax.
class "ex:MainClass" <<mandatory>> #F8978D|FCFFFC {
}
The class consists of (from left to right):
- Name, mandatory. If special characters are used, inverted commas must be used. Since, as is usual in RDF, the namespace should be included, the
:
is practically always used. - Obligation level, optional, enclosed in
<<
...>>
. - Colouring, optional. Hexadecimal code, introduced by
#
, where as in PlantUML colour gradients are possible via the special characters-|\/
. - Class body, optional. If the class has a class body, it may only be defined as shown. Other variants allowed by PlantUML are not supported.
By predicate we mean all RDF statements that are visible in the UML class diagram. The term comes from the Resource Description Framework, whose statements are formulated as a triple of subject, predicate and object (<s p o>
).
PlantUML has two types of predicates: those that are defined within a class and those that connect two classes of the ontology. A predicate should not be defined in both ways, as this is error-prone.
class "ex:MainClass" <<mandatory>> {
<<mandatory>> dct:title ~> rdfs:Literal [1..*]
<<recommended>> dct:modified ~> rdfs:Literal [0..1]
<<optional>> ex:owns ~> ex:MainClass [0..1]
}
These predicates are specified in the class diagram in the class body. Their definition consists (from left to right) of five elements:
- Obligation level, optional, enclosed in
<<
...>>
. - Name, mandatory. Quotation marks may not be used As is usual in RDF, the name should contain its namespace. In the
<s p o>
statement, this is the predicate. - ~>, mandatory. This string is required to separate the predicate from the object.
- Name of the object, mandatory. In the
<s p o>
statement, this is the object. Quotation marks must not be used, as usual in RDF, the name of the object should contain its namespace. - Cardinality, optional, enclosed in
[]
. Minimum and maximum are separated by..
. If the minimum and maximum cardinality are the same, it is sufficient to specify them once (e.g.:[1]
). If the maximum cardinality is unlimited,*
orn
can be used.
"ex:MainClass" -right-> "1..*" "ex:OtherClass" : <<mandatory>> ex:uses
These predicates are represented in the class diagram as a link between two classes. Their definition consists (from left to right) of five elements:
- Name of the subject, mandatory. Quotation marks are recommended. As usual in RDF, the name of the subject should include its namespace.
- -->, mandatory. This string is needed to separate the subject from the rest of the statement. In PlantUML, only an arrow pointing from left to right (
-->
by default) may be used. To influence the layout of the visual representation, the length of the arrow may be varied. Directional words are also supported:-left->
,-right->
,-up->
and-down->
. - Cardinality, optional, in quotes. Minimum and maximum are separated by
..
. If the minimum and maximum cardinality are the same, it is sufficient to specify them once (e.g.:"1"
). If the maximum cardinality is unlimited,*
orn
can be used. - Name of the object, mandatory. Quotation marks are recommended. As usual in RDF, the name of the object should include its namespace.
- Obligation level, optional, enclosed in
<<
...>>
. - Predicate name, mandatory. Quotation marks are recommended. As usual in RDF, the name of the predicate should include its namespace.
Classes and predicates can be extended by attributes. These are not shown in the visualisation of the PlantUML file. Instead, they are additional information that is needed to transformation the data model into other forms of representation.
class "ex:MainClass" <<mandatory>> {
<<mandatory>> dct:title ~> rdfs:Literal [1..*]
'@ rdfs:comment = Kommentar zu dct:title als Prädikat von ex:MainClass
}
'@ rdfs:label = Label von ex:MainClass
"ex:MainClass" -right-> "1..*" "ex:OtherClass" : <<mandatory>> ex:uses
'@ rdfs:comment = Attribute können mehrfach vorkommen.
'@ rdfs:comment = Z.B. für Zeilenumbrücke in Beschreibungen.
Attributes always refer to the preceding predicate or class. The attributes are key/value pairs defined as follows:
'@
, mandatory. While the apostrophe ('
) marks a line as a PlantUML comment, ensuring that the file remains compliant, the@
indicates that it is an attribute.- Tag, mandatory. The key of the key/value pair.
=
, mandatory. Separates the label from the value.- Value**, optional. The value of the key/value pair. Can be omitted if the value is empty.
"ex:SubClass" -up-|> "ex:SuperClass"
Inheritance information is stored in the data model and displayed in PlantUML, but it does not automatically affect other areas. For example, if a subclass shall have all predicates of its superclass, this must currently be modelled explicitly. The definition requires the following information:
- Name of subclass, mandatory. Inverted commas are recommended. As usual in RDF, the subclass name should include its namespace.
- --|>, mandatory. This string is needed to separate the subject from the rest of the statement. In PlantUML, only an arrow pointing from left to right (
--|>
by default) may be used. To influence the layout of the visual representation, the length of the arrow may be varied. Directional words are also supported:-left-|>
,-right-|>
,-up-|>
and-down-|>
. - Name of superclass, mandatory. Inverted commas are recommended. As usual in RDF, the superclass name should include its namespace.
The PlantUML file is transformed into a data structure using a Peggy grammar. The grammar is as minimal as possible and also works in the online version.
The data structure created by the grammar is used largely unchanged for the templates. The properties "..._names
" are added at the level of Ontology
, Class
and Predicate
. These are a list of the names of (unique) classes, attributes or predicates. This makes it easier, for example, to check for errors or to check whether a class has a certain predicate.
Classes are stored as objects in the Ontology
, where the name of the class is also the key used to access the class. Therefore the class name must be unique.
Attributes are also stored as objects in Class
and Predicate
. Access is via the attribute identifier. The value is always an array, so that an attribute identifier can also be used several times within a class or predicate.
If a class name is used more than once in the PlantUML file for initialisation, the previous version of the class is overwritten. In addition, an error message is stored in Ontology.warnings
.
If an inheritance is defined for an unknown class, this class is created. In addition, an error message is stored in Ontology.warnings
. The same happens if a predicate has an unknown class as domain or range.
These error messages help to find spelling mistakes or missing definitions in the model.
If you want to add more properties to the classes, predicates or the ontology, for example to simplify the templates or to extend the functionality, this should be done in the respective module definitions (Ontology.js
, Class.js
and Predicate.js
).
Handlebars is used as the templating engine. The Ontology
object is passed to the templates. New template files must be placed in the folder templates/
.
In the file generate.js
the following helper functions (Handlebars helper) are defined:
Helper | Input | Return | Description |
---|---|---|---|
eq |
v1, v2 | bool | Checks whether v1 is equal to v2 . |
ne |
v1, v2 | bool | Checks whether v1 is not equal to v2 . |
lt |
v1, v2 | bool | Checks whether v1 is less than v2 . |
gt |
v1, v2 | bool | Checks whether v1 is greater than v2 . |
lte |
v1, v2 | bool | Checks whether v1 is less than or equal to v2 . |
gte |
v1, v2 | bool | Checks whether v1 is greater than or equal to v2 . |
and |
v1, v2 | bool | Logical "AND" check between v1 and v2 . |
or |
v1, v2 | bool | Logical "OR" check between v1 and v2 . |
add |
v1, v2 | number | Adds v1 and v2 . |
lower |
v1 | string | Converts v1 to lower case. |
upper |
v1 | string | Converts v1 to uppercase. |
char1 |
v1 | string | Returns the first character of v1 . |
swap |
input, ...vN | string | Searches input in the list of vN s and returns the value following input . See example/details. |
join |
coll, text, ?uniq | string | Connects all (possibly unique) elements of coll to the content of text . Necessary to e.g. create a string from the array of an attribute value. uniq is false by default. See example/details. |
sort |
list | list | Sorts list alphabetically. It should be a collection of strings. |
sortByName |
list | list | Sorts list alphabetically. Consider the name of the entries. Useful for classes and predicates whose name is taken directly from PlantUML. |
sortByAttribute |
list, attribute | list | Sorts list alphabetically. To do this, look at the value of the attribute attribute . |
sortByRequirement |
list, order | list | Sorts list according to the order specified in order . For this purpose, consider the liability taken directly from PlantUML as requirement . See example/details. |
filterHasAttribute |
list, attribute | list | Returns a list containing only elements that have an attribute named attribute . |
Example/Details for swap
{{swap requirement "optional" "Optional" "recommended" "Empfohlen" "mandatory" "Pflicht"}}
Here
swap
is used to convert the English entries of the binding into German expressions.For example, the value "recommended" is passed to the function as
requirement
. The return value is the entry that follows "recommended".If the searched value is not found, the input value is passed unchanged.
If the value found is the last value in the list, the first value in the list is returned. In the first example, this would not be a useful behaviour. However, it is useful, for example, if you want to determine the next month:
{{swap currentMonth "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"}}
If "Dec" is passed here as
currentMonth
, "Jan" is returned as the next month.
Example/Details for join
join
matches the JavaScript function of the same name, but works for arrays as well as for objects. Applied to an object, all values of the object are joined.There are two typical use cases:
1)
{{join cardinality ".." true}}
Creates from the cardinality
{min: 0, max:2}
the string0..2
.The cardinality
{min: 1, max:1}
will result in1
, because oftrue
. If you ommittrue
set the option tofalse
({{join cardinality ".." false}}
), the result is1..1
.2)
{{join (lookup attributes "rdfs:comment") "<br>"}}
Connects all entries of therdfs:comment
attribute with an HTML line break (<br>
).Here
true
as the third parameter would remove duplicate values, e.g. several empty lines.
Example/Details for sortByRequirement
The parameter
list
expects a list of objects that have the propertyrequirement
. This means predicates and classes. The parameterorder
is a string that specifies the desired order. The order is based on the first letter of the respectiverequirement
entry.
{{sortByRequirement predicates "orm"}}
thus starts with predicates whoserequirement
starts witho
, followed byr
andm
. So first all "optional", then "recommended" followed by "mandatory".