What are the high added value uses of jArchi, the scripting solution integrated with Archi ? (August 2021)
Nicolas Figay
Model Manager | Enterprise Architecture & ArchiMate Advocate | Expert in MBSE, PLM, STEP Standards & Ontologies | Open Source Innovator(ArchiCG)
Introduction
When discovering the jArchi API, it looks first very exciting, as it means that you can automate a lot of thing.
However, the question arises very quick: what are the kind of scripts which will create an important value, performing tasks that you can’t do quickly with Archi? In particular, if you consider the advanced selector/filtering capabilities with the search function you can use in the model pane, or within the visualization pane, it appears that many of the task to think about or which are provided as examples are not providing so much value as making them manually is not that painful. So what are the painful tasks that can be considered when using Archi?
The current version of this article explores the following scenarios:
1- Reflecting a package/folder structure which is layers transversal
Do you know impact of having folders in place of packages (UML like) for ArchiMate? In facts, it means that you can't model a physical decomposition of your ArchiMate models, a folder being not a model element.Dealing with modularization for distributed work between teams is consequently difficult.
On turnaround I tried with Archi: to reflect a decomposition hierarchy in each layer folder + view folder, and to put the model element within the appropriate folder in order to create a kind of physical segregation.
I also create Grouping elements reflecting the hierarchy. So the modules can then be also reflected in the diagrams. A little bit tricky. Here come the interest of using jArchi.
I created a little script which, from a textual description of the hierarchy, creates all the folders plus the composition of groupings.
The textual description of a hierarchy is a set of lines created with a simple text editor. The level is indicated by the number of blanks at the beginning of the line. Don't use tabs and let's provide the appropriate number of blanks. E.g. if the hierarchy it to reflect the table of content of a document, you will have an input like the following one.
1-chapter 01
?1.1- chapter 1.1
? 1.1.1- chapter 1.1.1
? 1.1.2- chapter 1.1.2
?1.2- chapter 1.2
? 1.2.1- chapter 1.2.1
? 1.2.2- chapter 1.2.2
2-chapter 02
3-chapter 03
4-chapter 04
5-chapter 05
?5.1- chapter 5.1
? 5.1.1- chapter 5.1.1
? 5.1.2- chapter 5.1.2
Running the script on a target model (Cf. left figure) creates sub folders reflecting the hierarchy in all the ArchiMate layers ("Strategy", "Business", "Application", "Technology &Physical", "Motivation", "Implementation & Migration", "Others") and in the "Views" and "Relations" folders as illustrated by the next figure.
Groupings are also created with appropriate Composition relationships for reflecting this structure, shown through the visualizer ( figure below).
It is particularly useful for reflecting an architectural document for which you don't have model, and you would like to reflect in your model keeping track of the origin of the content of the you model, in particular if the model is not the reference.
The script, which is also available on is the following.
The input file with the textual hierarchy is "tm.txt", put on the jArchi script directory (_DIR_). If jArchi provides a file selector function for writing, I had some difficulties to find the way to select one for reading a file. So I used the java.io FileReader and BufferedReader functions through the mechanism provided by Nashorn for making them useable with javascript. These functions allowed me reading the input file line per line (line=br.readline()).
For a given line, having a level being the number of white space at the beginning, a folder is created (with createFolder from jArchi), being a subfolder of the one created with the previous line having a lower level.
It means we need when creating a folder its position in the created hierarchy of folder. For this, when creating the folder, we also store in an array the id of the created folder, as a same name can be used by several folders. The Ids are not available through the Archi user interface, but one is automatically created and managed by Archi. jArchi makes Id and Name available with .id and .name applied to any model element. The place the Id is stored in the array corresponds to the line level. The targeted containing folder is consequently the one with the previous index.
From its Id, we can select it with $("#id") and apply to it the createFolder function which will make effectively the created folder a subfolder of the targeted containing folder.
As the hierarchy is reflected in each layer, we have to create and managed it for all the layers. It it is the reason why an array of array is used, LastFolderForLevelsArray, initiated with an empty array for each top folder, top folders being managed by Archi itself and impossible to modify by the user. They also have an Id we retrieve and use for applying createFolder.
I'm not an experienced javascript developer, and this code is probably not something to be industrialized. However, it illustrates in few lines some of the capabilities provided by jArchi for navigating the model and creating elements. It is needed to become familiar with the mechanism provided by jArchi for navigating the model and selecting elements. It is quite powerful.
What is automated with this script is definitively something very long and time consuming if made by hand. It can be apply to many other inputs than a table of content of a document, in fact in any kind of hierarchy you want to reflect as a folder structure in you Archi environment: functional breakdown, organizational breakdown, etc.
2- Layout coloration or change of names
A second task which can be painful when having to consider many views are related to layout coloration or change of name according to a given property or a set of properties.
E.g. let’s consider you want to reflect though colors the degree of realization, captured in a property, of a targeted plateau, replacing the usual color code of ArchiMate (indicating layers) by other ones with a different meaning.
It is something I faced too in some project, such as Standard Interoperability PLM one (SIP), where ArchiMate and Archi were used, and which was quite useful for communicating about project advancement. It was a good complement to traditional tasks and Work Packages achievement follow up, allowing to discuss about the evolution of a planned operational environment.
Here a legend is to be added, in order allowing to interpret the color of the shared diagrams (creation by mean of a jArchi script?)
3- Checking if a set of rules applies
A third potential task is to check that a set of rules we want to be verified on the produced models are respected in terms of reification (i.e. mandatory explicit relationships which are mandatory on produced architectural views for the representations to be considered as completed), which is quite too difficult and time consuming when made manually. It implies to find a way to express the rules with Javascript first, plus way to realize the checking and to display the result.
jArchi allows such rules to be coded as script, without having to change the code of Archi.
Another alternative, the export to other environment supporting rules engines, can also be envisaged (e.g. OWL export and validation using Protégé + Pellet/Hermit/SWRLTab, or export to Grakn.AI). In some cases, if some Javascript libraries can be reused and integrated with Nashorn (used by Archi for jArchi), supporting rules descriptions and rules checking relying on these technologies, we can as well envisaged a fully integrated solution.
Here a link to a document discussing model conformity, highly related to such a usage.
Reporting or export
A fourth potential usage is related to production of reporting or export. The question here may arise concerning the interest of producing a script in place of producing an Archi Plugin. How easy it is producing reports or exports should be compare using the two approaches, and will depends on how easy it is easy to import complementary JavaScript libraries with Nashorn.?
5- OWL Export
One of the first targeted experimentation is the export of an ArchiMate model based on a derived ontology from ArchiMate.
A first OWL ontology I worked on is the one related on my LinkedIn article Linked Enterprises: about deriving a Web Ontology from the ArchiMate language ?.
Here, in order to defined the IRI, I used the terms as defined in the ArchiMate specifications and the Open Exchange format for ArchiMate. However, when using jArchi and the function giving the type of a selected model element, the characters are not exactly the same, and you can have some extra objects which are Archi specific, e.g. those for sketchs or references to a diagram. So for my first OWL export script, I decided to target the export of Archi content, and derived a dedicated OWL file for Archi. I will later on performed a mapping between both ontology in order to illustrate ontology mapping in OWL.
The proposed script consists in exporting:
So it is needed to define the way to access to all these object, constituting a reusable template for other kind of similar exports for other languages used by other knowledge platforms.
As soon as the new version of Archi and corresponding jArchi will be released, considering specialization of the modeling constructs, export of the specializations.
For the extensions engineering, i.e. properties and specialisation, it can be considered that they will be first defined as part of the ontology, and once validated created inside the archi environment in order to be used. Then, export will be part of the ontology. It is in particular the case for data properties, which will appear as annotation if not defined as data properties.
The following screenshots are showing what is obtained when opening the exported OWL with Protégé, using Ontograph.
In the following figure we can see a model element, of type technology-service, with the properties.
In the next figure, we have the properties of the view on visual representation of the object belongs to.
In the following figure, we have a visual representation of the object, with the label. The properties of the model element are not created for the visual object, they could eventually be derived through inferencing, or added as an option of the export.
领英推荐
So we have the whole enterprise architecture model export in OWL, which can then allow to take advantage of semantic web technologies, in terms of logic validation and inferencing. It is also possible to aggregate ArchiMate models and other models produced with other languages, more detailed than Archimate and allowing to extend the models and to take advantage of other technologies (e.g. BPMN with process simulation, or UML with software design). This this the purpose of other articles.
6- Vis.js export (advanced visualization)
A second experimentation will be related to visualization, with creation of JSON data which can be reused with advanced visualization using Vis.js, as explored in the LinkedIn article?Semantic Cartography with ArchiMate: a first step for Enterprise Navigation System.
Archi already comes with visualization features: diagrams and graph visualization centered on on selected model element (corresponding to an entity). E.g. let's consider the following diagram displaying the semantic web identified product components, which are candidate for usage for building semantic cartography of the Enterprise, combining ontology and advanced visualization technics.
With the Archi visualisation, you can select on model element and display the graph of all the connected model elements through a relation, incoming or outgoing, and extend the graph up to 6 depth levels. You can also filter this graph according to the type of elements or relations. The layout is automated. However the produced graph is not forced constrained, and you can't parameter the kind of objects displayed, or extend what is displayed in the graph, such as views and their content (nodes and links), and then links to the model elements they are representing (we are not discussing here the value of visualizing such information, it will be the subject of other articles or scientific papers). Also, what it we don't want a graph centered on a single model element (neighborhood) but a graph built from other constructors? What if we also want to represent relations as nodes, with arcs "from" and "to" pointing on source and target elements? Such needs motivate the creation of a script for exporting some content of the Archi Models for usage in other graph visualization tools. I've been studying vis.js as one of the advanced visualization technics producing force constrained graphs which can be visualized on a web browser, relying on javascript open source libraries. It allows to display big graphs (Ok here some figures should be given but it is part of an ongoing study for indicating what big means precisely here). Relying on datasets and dataviews features provided by vis.js, interactive filtering functionalities can also be considered for the produced export.
A first developed script (other will come) extracts from a diagram all the displayed elements in order to produce an alternative visual representation of the diagram. It is an alpha version not yet published. In this version, the relations are displayed as arcs. In a future version, it will be possible from the graph viewer to indicate if relations will be displayed as nodes or arcs. It is also plan to show the properties of each nodes, to parameter the labeling of the graph elements and to use clusters?for composition, groupings, views or folders.
/
?* View export as Vis.js?
?* Author: Nicolas Figay 2021
?* Version: 0.1
?* This script creates a vis.js graph
*/
var ArchiMateRelationArcs =
["arrows:{to:? ? {enabled:false,type:'arrow'},\
? ? ? ? ? from:? {enabled:false,type:'arrow'}},\
? ? ? ? ? dashes:[5,5]",//Access
?"arrows:{from:{enabled:true,type:'diamond'}}",
?"arrows:{flow to:{enabled: true,type:'vee'}},dashes:[5,5]",
?"arrows:{from:{enabled:true,type:'diamond'}}",
?"arrows:{from:{enabled:true,type:'circle',arrowStrikethrough:false},\
? ? ? ? ? to:? {enabled:true,type:'vee',? ?arrowStrikethrough:false}}",
?"arrows:{to: {enabled:true,type:'arrow'}},dashes:[5,5]",
?"arrows:{to: {enabled:true,type:'curve'}}",
?"arrows:{to: {enabled:true,type:'triangle'}},dashes:[2,2]",
?"arrows:{to: {enabled:true,type:'triangle'}}",
?"arrows:{to: {enabled:true,type:'vee'}}",
?"arrows:{to: {enabled:true,type:'arrow',arrowStrikethrough:false}}"
];
//endPointOffset:{from:0,to:0} to add for arrows closer to the object? To be investigated
console.log("Vis.js export Script");
load(__DIR__ + "lib/archimate.js");
var DIR = './img/archimate/';
var imageExtension="svg";
var ArchiMateRelationNames =[ "Access", "Composition", "Flow", "Aggregation", "Assignment", "Influence", "Association", "Realization", "Specialization", "Triggering", "Serving"];
//Archi always uses one of the direction of each relation, with an associated label contained in the following array, and corresponding to the relations of the previous array with the same order
var orientedArchiRelationNames =[ "accesses", "is composed of", "flows to", "aggregates", "is assigned to", "influences", "is associated with", "realizes", "is a specialization of", "triggers", "serves"];
String.prototype.capitalize = function() {
? ? return this.charAt(0).toUpperCase() + this.slice(1);
}
var FileWriter=Java.type("java.io.FileWriter");
var views = selection.filter("archimate-diagram-model");
if (views.length == 0){console.log ("No view selected -> let's select one or several");}
views.each (function(view){
// Saving the diagram as PNG
? ? ?var bytes = $.model.renderViewAsBase64(view, "PNG", {scale: 1, margin: 20});
? ? ?var fileName = window.promptSaveFile( { title: "Save View", filterExtensions: [ "*.png" ], fileName: view.name + ".png" } );
? ? ?if(fileName) {$.fs.writeFile(fileName, bytes, "BASE64");}
? ? ?imageName=fileName.replace(/^.*(\\|\/|\:)/, '');
? ? ?console.log(imageName);?
? ? var fileName = window.promptSaveFile( { title: "Vis.js export", filterExtensions: [ "*.html" ], fileName: view.name + ".html" } );
? ? if(fileName) {
? ? // for the diagram let's create a node and a cluster containing all the nodes representing the element of the diagram
? ? ? ?var fw = new FileWriter(fileName);
? ? ? ?fw.write("<!DOCTYPE html>");
? ? ? ?fw.write('<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">');
? ? ? ?fw.write("<title>"+ view.name+"</title>");
? ? ? ?fw.write ('<script type:"text/javascript" src="https://unpkg.com/vis-data@latest/peer/umd/vis-data.min.js"></script>\n');
? ? ? ?fw.write ('<script type:"text/javascript" src="https://unpkg.com/vis-network@latest/peer/umd/vis-network.min.js"></script>\n');
? ? ? ?fw.write ('<script type:"text/javascript" src="https://unpkg.com/vis-network@latest/peer/umd/vis-network.min.js"></script>\n');
? ? ? ?fw.write ('<link rel="stylesheet" type:"text/css" />\n');??
? ? ? ?fw.write ('</head>');
? ? ? ?fw.write('<body onload="draw()" class="lang-fr">');
? ? ? ?fw.write ('<div style="width:100%;height:400px;">\n');?
? ? ? ?fw.write ('<div id="mydiagram" style="width:30%; height:100%; float:left; border-width: 2px; border-style: inset; border-color:black" ><img style="width:100%;height:auto;" src="');
? ? ? ?fw.write (imageName);
? ? ? ?fw.write ('" /></div>\n');?
? ? ? ?fw.write ('<div id="mynetwork1" style="width:30%; height:100%; float:left; border-width: 2px; border-style: inset; border-color:black" ></div>\n');?
? ? ? ?fw.write ('<div id="mynetwork2" style="width:30%; height:100%; float:left; border-width: 2px; border-style: inset; border-color:black" ></div>\n');?
? ? ? ?fw.write ('</div>\n');
? ? ? ?
? ? ? ?fw.write ('<script type:"text/javascript">\n');
? ? ? ?fw.write("function draw() {");
? ? ? ?fw.write ('var nodes = new vis.DataSet([]);\n');
? ? ? ?fw.write ('var edges = new vis.DataSet([]);\n');
??
? ? ? ?fw.write ('nodes.add({id:"');
? ? ? ?fw.write (view.id);
? ? ? ?fw.write ('"');
? ? ? ?fw.write (",shape:'image', label:'");
? ? ? ?fw.write (view.name);
? ? ? ?fw.write ("',image:");
? ? ? ?fw.write ('"');? ? ? ?
? ? ? ?fw.write (DIR);
? ? ? ?fw.write ("view.");
? ? ? ?fw.write (imageExtension);
? ? ? ?fw.write ('"');
? ? ? ?fw.write (', EH4I_Type:"View"');
? ? ? ?fw.write (', viewpoint:"');
? ? ? ?fw.write (view.viewpoint.id);
? ? ? ?fw.write ('"');
? ? ? ?fw.write ("});")
? ? ? ?fw.write ("\n");
? ? ? ?did="#"+view.id;?
?
? ? ? ?$(did).find().filter('element').each(function(node) {
? ? ? ? ? imageName=node.type;
? ? ? ? ? switch (node.type) {
? ? ? ? ? ? ? //for any operation to be made before creating a node in vis.js
? ? ? ? ? ? ? case "junction":
? ? ? ? ? ? ? ? imageName=node.concept.junctionType+"-junction";
? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ??
? ? ? ? ? }
// to create a node in vis.js
? ? ? ? ?fw.write ('nodes.add({id:"');
? ? ? ? ?fw.write (node.id);
? ? ? ? ?fw.write ('"');
? ? ? ? ?fw.write (",shape:'image', label:'");
? ? ? ? ?fw.write (node.name);
? ? ? ? ?fw.write ("',image:");
? ? ? ? ?fw.write ('"');
? ? ? ? ?fw.write (DIR);
? ? ? ? ?fw.write (imageName);
? ? ? ? ?fw.write (".");
? ? ? ? ?fw.write (imageExtension);
? ? ? ? ?fw.write ('"');
? ? ? ? ?fw.write (", title: ");?
? ? ? ? ?fw.write ('"');
? ? ? ? ?fw.write (imageName);
? ? ? ? ?fw.write ('"');
? ? ? ? ?fw.write (', EH4I_Type:"Entity"');
? ? ? ? ?fw.write ("});")
? ? ? ? ?fw.write ("\n");
? ? ? ?});?
? ??
? ? ? ?$(did).find().filter('relationship').each(function(relation) {
? ? ? ? ? ? var myRelation=relation.type.replace(/-relationship/g, "").capitalize();
? ? ? ? ? ? var myArc=ArchiMateRelationArcs[ArchiMateRelationNames.indexOf(myRelation)];
? ? ? ? ? ? myArc= myArc.replace(/\s+/g,"");
? ? ? ? ? ? var myLink=orientedArchiRelationNames[ArchiMateRelationNames.indexOf(myRelation)];
? ? ? ? ? ? var myLabel=relation.type;
? ? ? ? ? ? switch (relation.type) {
? ? ? ? ? ? ? ? case "access-relationship":
? ? ? ? ? ? ? ? ? ? switch (relation.concept.accessType){
? ? ? ? ? ? ? ? ? ? ? ? case "write":? myArc=myArc.replace(/to:{enabled:false/,"to:{enabled:true");? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?break;
? ? ? ? ? ? ? ? ? ? ? ? case "read" :? myArc=myArc.replace(/from:{enabled:false/,"from:{enabled:true");break;
? ? ? ? ? ? ? ? ? ? ? ? case "readwrite": myArc=myArc.replace(/false/g,"true");break;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? var myLabel=relation.concept.AccessType;
? ? ? ? ? ? ? ?default:? ? ? ? ? ?
? ? ? ? ? ? }? ? ? ?
? ? ? ? ? ? fw.write ('nodes.add({id:"');
? ? ? ? ? ? fw.write (relation.id);
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (",shape:'image'");
? ? ? ? ? ? fw.write (", label:'");
? ? ? ? ? ? fw.write (relation.name);
? ? ? ? ? ? fw.write ("',image:");
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (DIR);
? ? ? ? ? ? fw.write (myRelation);
? ? ? ? ? ? fw.write (".");
? ? ? ? ? ? fw.write (imageExtension);
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (', EH4I_Type:"Relation"');
? ? ? ? ? ? fw.write (", label: ");?
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (myLabel);
? ? ? ? ? ? fw.write ('"');?
? ? ? ? ? ? fw.write ("});")
? ? ? ? ? ? fw.write ("\n");
? ? ? ? ? ? fw.write ("edges.add({from: ");
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (relation.id);
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (", to: ");
? ? ? ? ? ? fw.write ('"');? ? ? ? ? ? ? ? ?
? ? ? ? ? ? fw.write (relation.target.id);
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (', EH4I_Type:"Role"');
? ? ? ? ? ? fw.write (',');
? ? ? ? ? ? fw.write ('arrows: {to:{enabled: true}}');?
? ? ? ? ? ? fw.write (", label: 'target'");?
? ? ? ? ? ? fw.write ("});");
? ? ? ? ? ? fw.write ("\n");
? ? ? ? ? ? fw.write ("edges.add({from: ");
? ? ? ? ? ? fw.write ('"');? ? ? ? ? ? ? ? ?
? ? ? ? ? ? fw.write (relation.id);
? ? ? ? ? ? fw.write ('"');? ? ? ? ? ? ? ? ?
? ? ? ? ? ? fw.write (", to: ");
? ? ? ? ? ? fw.write ('"');? ? ? ? ? ? ? ? ?
? ? ? ? ? ? fw.write (relation.source.id);
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (', EH4I_Type:"Role"');
? ? ? ? ? ? fw.write (',');
? ? ? ? ? ? fw.write ("arrows: {to:{enabled: true}}");
? ? ? ? ? ? fw.write (", label: 'source'");? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? fw.write ("});");
? ? ? ? ? ? fw.write ("\n");
if (false){??
};
? ? ? ? ? ? fw.write ("edges.add({from: ");
? ? ? ? ? ? fw.write ('"');? ? ? ? ? ? ? ? ?
? ? ? ? ? ? fw.write (relation.source.id);
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (", to: ");
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (relation.target.id);
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (", title: ");?
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (myLink);
? ? ? ? ? ? fw.write ('"');?
? ? ? ? ? ? fw.write (", label: ");?
? ? ? ? ? ? fw.write ('"');
? ? ? ? ? ? fw.write (myLabel);
? ? ? ? ? ? fw.write ('"');?
? ? ? ? ? ? fw.write (', EH4I_Type:"Link"');
? ? ? ? ? ? fw.write (',');
? ? ? ? ? ? fw.write ( myArc);? ? ? ? ? ? ? ??
? ? ? ? ? ? fw.write ("});");
? ? ? ? ? ? fw.write ("\n");
? ? ? ? ? ? if(relation.type==="composition-relationship"){
? ? ? ? ? ? ? ? ?fw.write ('nodes.update({id:"');
? ? ? ? ? ? ? ? ?fw.write (relation.target.id);
? ? ? ? ? ? ? ? ?fw.write ('", partOf:"');
? ? ? ? ? ? ? ? ?fw.write (relation.source.id);
? ? ? ? ? ? ? ? ?fw.write ('"});');
? ? ? ? ? ? ? ? ?fw.write ("\n");
? ? ? ? ? ? }
? ? ? ?});?
? ? ? ?fw.write ("\n");
? ? ? ?
? ? ? ?fw.write('var nodes1 = new vis.DataView(nodes, {filter: function (item) { return (item.EH4I_Type == "Entity");}});\n');
? ? ? ?fw.write('var edges1 = new vis.DataView(edges, {filter: function (item) { return (item.EH4I_Type == "Link");}});\n');
? ? ? ?fw.write('var nodes2 = new vis.DataView(nodes, {filter: function (item) { return (item.EH4I_Type == "Entity" || item.EH4I_Type == "Relation" );}});\n');
? ? ? ?fw.write('var edges2 = new vis.DataView(edges, {filter: function (item) { return (item.EH4I_Type == "Role");}});\n');
? ? ? ?fw.write ('var container1 = document.getElementById("mynetwork1");\n');
? ? ? ?fw.write ('var data1 = { nodes: nodes1, edges: edges1 };\n');
? ??
? ? ? ?fw.write('var options1={joinCondition:function(nodeOptions) {return nodeOptions.partOf === "fdcb8dad-91b2-450d-81a8-e922d34f978e";}};');
? ? ? ?fw.write ('var network1 = new vis.Network(container1, data1, options1);\n');
? ? ? ?fw.write('network1.clustering.cluster(options1);');
? ? ? ?fw.write('network1.on("click",function(params) {console.log(params);if (params.nodes.length == 1) {if (network1.isCluster(params.nodes[0])==true) {network1.openCluster(params.nodes[0]);}}});');
? ? ? ?
? ? ? ?fw.write ('var container2 = document.getElementById("mynetwork2");\n');
? ? ? ?fw.write ('var data2 = { nodes: nodes2, edges: edges2 };\n');
? ? ? ?fw.write ('var options2 = {};\n');
? ? ? ?fw.write ('var network2 = new vis.Network(container2, data2, options2);\n');
? ? ? ?fw.write("};")
? ? ? ?fw.write ('</script></body></html>\n');
? ? ? ? ?
? ? ? ?fw.close();? // forgetting to close it results in a truncated file
? ? ? ?}
? ? }
);
*
7- PlantUML ArchiMate export (advanced visualization)
When creating a view, arrange properly the diagram can be a tedious task, as automated layouts is not supported by Archi (here you can have a look to what a solution like Yed allows). A solution like PlantUML, from a textual description, will create automatically a diagram with automatic layouts, to be integrated in a documentation. So if we can derived such a textual description from an Archi model and the defined views, it could be the opportunity to save some time. It can also be envisaged to derived UML representations (e.g. sequence diagrams) from active elements of an ArchiMate model, as illustrated by the following link.
A first script is about displaying the different constructs of the ArchiMate language in plantUML (here jArchi is not needed, as it is not related to a given model).
For each construct of the language, an object is created with as name "a " plus the name of the construct using naming of the ArchiMate Relationships matrix provided with the language specification (also available in Archi). The script is derived from the one used for the ArchiMate language viewer relying on vis.js. It can also be run with jArchi.
The things are different when willing to export (parts of ) the model. The initial idea: to start from an existing view (diagram), and generate PlantUml archimate drawing from the visual objects in this view, considering that the layout will be automated and rely on PlantUml archimate capabilities, which could be Archi Diagramming different (e.g. nested visual objects are supported only with Grouping and Group for PlantUml archimate). This implies a particular way to navigate a diagram content for generating the PlantUml description.
The following figures illustrate two kinds of model representations.
Respectively, the outcome is:
?* PlantUML View export as Graph Script (PlantUML-V2G)
?* Author: Nicolas Figay 2020
?* Version: 0.1
?* This script creates a .puml file with all elements in the selected views - one file per view
?* A simple graph is produced, no nesting produced
*/
?
console.log("PlantUML View export Script");
load(__DIR__ + "lib/archimate.js");
String.prototype.capitalize = function() {
? ? return this.charAt(0).toUpperCase() + this.slice(1);
}
var FileWriter=Java.type("java.io.FileWriter");
var views = selection.filter("archimate-diagram-model");
if (views.length == 0){console.log ("No view selected -> let's select one or several");}
views.each (function(view){
? ? var fileName = window.promptSaveFile( { title: "Plant UML export", filterExtensions: [ "*.puml" ], fileName: view.name + ".puml" } );
? ? if(fileName) {
? ? ? ?var fw = new FileWriter(fileName);
? ? ? ?fw.write("@startuml\n");
? ? ? ?fw.write("!includeurl https://raw.githubusercontent.com/ebbypeter/Archimate-PlantUML/master/Archimate.puml\n");
? ? ? ?fw.write("LAYOUT_AS_SKETCH()\n");?
? ? ? ?fw.write("LAYOUT_LEFT_RIGHT\n");
? ? ? ?fw.write("'LAYOUT_TOP_DOWN\n");
? ? ? ?fw.write ("Grouping(");
? ? ? ?fw.write (view.id);
? ? ? ?fw.write (',"');
? ? ? ?fw.write (view.name);
? ? ? ?fw.write ("::ArchiMate view from Archi generated with plantuml-V2G 0.1");
? ? ? ?fw.write ('"){');
? ? ? ?fw.write ("\n");
? ? ? ?did="#"+view.id;?
? ? ? ?$(did).find().filter('element').each(function(node) {
? ? ? ? ? switch (node.type) {
? ? ? ? ? ? ?case 'junction':
? ? ? ? ? ? ? ? // At this time jArchi is not providing yet the access to the type of a Junction
? ? ? ? ? ? ? ? // A temporary workaround for dealing with it, here again a dedicated property e.g. puml.junctionType which as value "And" or "Or"
? ? ? ? ? ? ? ? index=ja_ArchiMateObjects.indexOf(node.type);
? ? ? ? ? ? ? ? puml=puml_ArchiMateObjects[index]; //I.e. "Junction_Or" - To be change with "Junction_And" if property.junctionType value? is equal to "And"? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ?case 'location':
? ? ? ? ? ? ? ? // Could be Business_Location or Other_Location for PlantUML-ArchiMate
? ? ? ? ? ? ? ? // A way to take it into account is to define a dedicated property, e.g. puml.location which as value "Business" in order to use Business in place of Other (default)?
? ? ? ? ? ? ? ? puml="Other_Location";
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ? index=ja_ArchiMateObjects.indexOf(node.type);
? ? ? ? ? ? ? ? puml=puml_ArchiMateObjects[index];?
? ? ? ? ? }
? ? ? ? ? fw.write(puml);
? ? ? ? ? fw.write('(');
? ? ? ? ? fw.write(node.id.replace(/-/g, "_"));
? ? ? ? ? fw.write(',"');
? ? ? ? ? fw.write(node.name);? ?
? ? ? ? ? fw.write ('")\n');??
? ? ? ?});?
? ??
? ? ? ?$(did).find().filter('relationship').each(function(relation) {? ? ?
? ? ? ? ? switch (relation.type) {
? ? ? ? ? ? ?case "access-relationship":
? ? ? ? ? ? ? //at this time not possible to obtain the type, i.e. read, write, etc. with jArchi
? ? ? ? ? ? ?default:
? ? ? ? ? ? ? ? fw.write("Rel_");
? ? ? ? ? ? ? ? fw.write(relation.type.replace(/-relationship/g, "").capitalize());
? ? ? ? ? ? ? ? fw.write("(");
? ? ? ? ? ? ? ? fw.write(relation.source.id.replace(/-/g, "_"));
? ? ? ? ? ? ? ? fw.write(",");
? ? ? ? ? ? ? ? fw.write(relation.target.id.replace(/-/g, "_"));
? ? ? ? ? ? ? ? fw.write(',"');
? ? ? ? ? ? ? ? fw.write(relation.name);
? ? ? ? ? ? ? ? fw.write('")');
? ? ? ? ? ? ? ? fw.write ("\n");
? ? ? ? ? ? }
? ? ? ?});?
? ? ? ?fw.write ("}\n");
? ? ? ?fw.write("@enduml\n");
? ? ? ?fw.close();? // forgetting to close it results in a truncated file
? ? ? ?}
? ? }
);
*
This second script creates the nested image:
/
?* PlantUML View export as Graph with Nesting Script (PlantUML-V2NG)
?* Author: Nicolas Figay 2020
?* Version: 0.1
?* This script creates a .puml file with all elements in the selected views - one file per view
?* A graph is produced,? with Group and Grouping elements represented as nesting elements
*/
?
console.log("PlantUML View export as Graph with Nesting Script (PlantUML-V2NG)");
load(__DIR__ + "lib/archimate.js");
String.prototype.capitalize = function() {
? ? return this.charAt(0).toUpperCase() + this.slice(1);
}
let displayChildren = function f(object){
var did="#"+object.id;?
? ?$(did).children().filter('element').each(function(node) {
? ? ? switch (node.type) {
? ? ? ? ?case 'junction':
? ? ? ? ? ? // At this time jArchi is not providing yet the access to the type of a Junction
? ? ? ? ? ? // A temporary workaround for dealing with it, here again a dedicated property e.g. puml.junctionType which as value "And" or "Or"
? ? ? ? ? ? index=ja_ArchiMateObjects.indexOf(node.type);
? ? ? ? ? ? puml_junction=puml_ArchiMateObjects[index]; //I.e. "Junction_Or" - To be change with "Junction_And" if property.junctionType value? is equal to "And"? ? ? ? ?
? ? ? ? ? ? fw.write(puml_junction);? ? ??
? ? ? ? ? ? break;
? ? ? ? ?case 'location':
? ? ? ? ? ? // Could be Business_Location or Other_Location for PlantUML-ArchiMate
? ? ? ? ? ? // A way to take it into account is to define a dedicated property, e.g. puml.location which as value "Business" in order to use Business in place of Other (default)?
? ? ? ? ? ? fw.write("Other_Location");
? ? ? ? ? ? break;
? ? ? ? ?default:
? ? ? ? ? ? index=ja_ArchiMateObjects.indexOf(node.type);
? ? ? ? ? ? fw.write(puml_ArchiMateObjects[index]);
? ? ? ?}?
? ? ? fw.write('('+node.id.replace(/-/g, "_")+',"'+node.name+'")');
? ? ? if (node.type == "group" || node.type=="grouping"){
? ? ? ? ?fw.write("{\n");
? ? ? ? ?viewDisplayChildren(node);
? ? ? ? ?fw.write("}\n")
? ? ? ? ?} else {
? ? ? ? ? ?fw.write("\n")
? ? ? ? ? ?viewDisplayChildren(node);
? ? ? ? ?}
? ?});?
}
var FileWriter=Java.type("java.io.FileWriter");
var view = selection.filter("archimate-diagram-model").first();
let viewDisplayChildren=displayChildren;
var fileName = window.promptSaveFile( { title: "Plant UML export", filterExtensions: [ "*.puml" ], fileName: view.name + ".puml" } );
if(fileName) {
?// Write to file
? ?var fw = new FileWriter(fileName);
? ?fw.write("@startuml\n");
? ?fw.write("!includeurl https://raw.githubusercontent.com/ebbypeter/Archimate-PlantUML/master/Archimate.puml\n");
? ?fw.write("LAYOUT_AS_SKETCH()\n");?
? ?fw.write("LAYOUT_LEFT_RIGHT\n");
? ?fw.write("'LAYOUT_TOP_DOWN\n");
? ?viewDisplayChildren(view);??
? ?did="#"+view.id;?
? ?$(did).find().filter('relationship').each(function(relation) {? ?
? ? ? ?switch (relation.type) {
? ? ? ? ? case "composition-relationship":
? ? ? ? ? ? ?if (relation.source.concept.type=="grouping") {
? ? ? ? ? //? Don't draw if already reflected by nested groups or grouping on the view (archi like)
? ? ? ? ? ? ? ? console.log ("relation.source:"+relation.source);?
? ? ? ? ? ? ? ? console.log ("relation.source.id:"+relation.source.id);
? ? ? ? ? ? ? ? var grouping ="#"+relation.source.id;??
? ? ? ? ? ? ? ? console.log ("children:"+$(grouping).find());
? ? ? ? ? ? ? ? console.log ("relation.source.concept.type:"+relation.source.concept.type);
? ? ? ? ? ? ? ? console.log ("relation.target.concept.type:"+relation.target.concept.type);
? ? ? ? ? ? ? ? var groupingChildren=[];
? ? ? ? ? ? ? ? $(grouping).find().each(function(child){
? ? ? ? ? ? ? ? groupingChildren.push(child.id);
? ? ? ? ? ? ? ? });
? ? ? ? ? ? ? ? console.log("target.id:" + relation.target.id);
? ? ? ? ? ? ? ? console.log("children:" + groupingChildren);
? ? ? ? ? ? ? ? var isAChild=groupingChildren.lastIndexOf(relation.target.id);
? ? ? ? ? ? ? ? console.log ("the target being nested in the source is:"+ isAChild);
? ? ? ? ? ? ? ? if (isAChild < 0){
? ? ? ? ? ? ? ? ? ? ? ? console.log ("we create");
? ? ? ? ? ? ? ? ? ? ? ? ?puml_Relation="Rel_"+relation.type.replace(/-relationship/g, "").capitalize()
? ? ? ? ? ? ? ? ? ? ? ? ? ? +"("+relation.source.id.replace(/-/g, "_")+","+relation.target.id.replace(/-/g, "_")+',"'+relation.name+'")'+"\n";
? ? ? ? ? ? ? ? ? ? ? ? ?fw.write (puml_Relation);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ?}
? ? ? ? ? ? ?break;
? ? ? ? default:
? ? ? ? ? ?puml_Relation="Rel_"+relation.type.replace(/-relationship/g, "").capitalize()
? ? ? ? ? ? ? ? ? ? ? ?+"("+relation.source.id.replace(/-/g, "_")+","+relation.target.id.replace(/-/g, "_")+',"'+relation.name+'")'+"\n";
? ? ? ? ? ?fw.write (puml_Relation);
? ? ?}
? ?});?
? ?fw.write("@enduml\n");
? ?fw.close();? // forgetting to close it results in a truncated file
}
*
8- Excel export and import
An official plugin is proposed for Excel Export. However the reverse is not available, and the way the export is made could not be the one you are expecting. The ??Forms?? plugin also supports some Excel export. Some proposals have been realized with JArchi.
The reuse of legacy Javascript libraries related to Excel or CSV is not so easy, due to configuration management issues, related to the used version of the Javascript language, but also of the used version of Nashorn, which is today deprecated and will not necessarily supports the evolutions of Javascript. The question arises concerning using JArchi or writing?a plugin, in particular if the efforts are important for writing the export and for identifying the easy to reuse libraries. Another point to consider: it seems quite difficult to create forms with jArchi due to Nashorn philosophy. Writing forms for Excel import or export could be very useful for their parameterization. So relevance of relying on jArchi for Excel Export/Import is not really established.
9- Generating ArchiMate Allowed Relations tables
If willing to automate generation of constraints reflecting the allowed or not allowed relationships, or to compare implementation with specification, an issue is that the table is most of the time available for reading, not for usage by automates. So taking the table as input for automation is very time consuming and error prone. The two following figures are how Archi and how the ArchiMate specifications provide the information, as table dedicated to usage by people which can't be interpreted by machines.
One potential usage of jArchi: to create automatically this table as an Excel file (using CSV) from what is implemented in Archi (it also implies that Archi is fully aligned with the last version of the specification). It can be made with few lines of code for creating a table you can then use with a worksheet office application. It can be done with the function isAllowedRelationship.
var allowed = $.model.isAllowedRelationship(myRelation,elementSource.type ,elementTarget.type);
It return true when the relationship is allowed for the type of the source and for the type of the target. So the idea is to create model elements for all the element type, and then to iterate for capturing what is allowed or not and to save it as CSV file. A matrix of each relation can also be produced, with 1 or 0, when the relation is allowed or not. The outcome is the following.
Similarly, constraint for semantic Web in OWL or for system/software modeling in OCL can be generated relying on Archi using the isAllowedRelationship function.
10- A little bit more about jArchi
Progressively, the Archi libraries, initially written in Java, are made accessible in JavaScript relying on Nashorn capabilities. It means that the different objects are made available with a set of operations for creating collections or selection through filters in javascript. The previous diagram reflects what jArchi makes available (cf. the following resource).
Some points you should take care about
When you use jArchi, you use the JavaScript language, but with the possible reuse of legacy Java Classes. Or some differences exist between Java and javascript, in particular how to deal with inheritance, which is more prototyped based than class based.
So a particular attention is to be payed concerning programming style and inheritance.
Conclusion
So here, from my personal experience, are the first potential usages of jArchi scripts I identified which could really create added value, in particular because it should help saving time for useful operations.?
Experimentations will be shared through this article or future ones.
Archi is a great tool, and jArchi makes it even greater, for supporting Enterprise Architecture Modeling based on both an open and de facto standards. It benefits to the whole enterprise modeling community, for making emerge effective shared best practices, creating value for enterprises. I've been amazed by the ergonomic design of Archi since I discovered it, and jArchi reflects the incredible design qualities of Archi, supporting model usage through easy to create selections and filters. Let's support their authors becoming a Patron.
Don’t hesitate reacting and sharing you own ideas and experimentations. And don’t hesitate to like or to share if you think this article brings value ;)
Let’s follow.
Senior Technical Manager (Architect) at Cloud.ru
1 年Nice article! Thank you, Dr. Nicolas Figay!
Architecte Entreprise freelance chez CADREHO
2 年Great work
Managing Partner at ITMC AG - living strategy
3 年Hi Nicolas, Very good ideas, I share a lot of your thoughts and your enthusiasm for Archi/jArchi. Regarding point 8 (Excel import & export) I've published an ajs script on GitHub that works as the basis of an Excel roundtrips. It builds on existing scripts but also works in the German (maybe also in the French) locale and handles special characters correctly: https://gist.github.com/trischbeck/a4e1b6bb2711cccb558a0c5fb269f6df. In contrast to the Excel plugin this script also includes all properties associated with an element in separate columns of the spreadsheet. Kind regards, Thomas
Business Architect - Modeling expertise with ArchiMate - System engineering enthusiast CESAMES & ARCADIA - PECB ISO27001 Lead implementer
3 年Philippe C. Pascal Bleuyard
R&D Project Manager | Architect at IRT SystemX
4 年Thank you, very interesting !