Prechádzať zdrojové kódy

DTDesign plugin: many changes

Joeri Exelmans 2 rokov pred
rodič
commit
eedb3989b1
1 zmenil súbory, kde vykonal 125 pridanie a 93 odobranie
  1. 125 93
      src/main/webapp/myPlugins/dtdesign.js

+ 125 - 93
src/main/webapp/myPlugins/dtdesign.js

@@ -3,33 +3,36 @@ const EXPECTED_BACKEND_VERSION = 3; // expected backend version
 const SPARQL_SERVER   = "http://localhost:3030"
 const SPARQL_ENDPOINT = "/SystemDesignOntology2Layers/sparql"
 
-const dropVocabularyPrefix = str => str.substring(30);
+const dropVocabularyPrefix = str => str.substring(41);
 const dropDescriptionPrefix = str => str.substring(30);
+const dropArtifactPrefix = str => str.substring(41);
 
-// Query that navigates the given link from the given source element, and returns the IRI and most-concrete-type of the target element.
-const getOutgoingLink = (iri, link_type) => `\
+const QUERIES = {
+  // Query that navigates the given link from the given source element, and returns the IRI and most-concrete-type of the target element.
+  // If `reverse` is true, then incoming links are returned instead.
+  getOutgoingLink: (iri, link_type, reverse=false) => `\
 PREFIX object_diagram: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>
 PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
 
 SELECT DISTINCT ?element ?type
 WHERE {
-  <${iri}> <${link_type}> ?element .
+  ${reverse ? `?element <${link_type}> <${iri}>` : `<${iri}> <${link_type}> ?element`}.
   ?element a ?type .
   ?type rdfs:subClassOf object_diagram:Object .
-  NOT EXISTS {
+  FILTER(NOT EXISTS {
     ?more_concrete_type rdfs:subClassOf ?type .
     ?element a ?more_concrete_type .
     FILTER(?more_concrete_type != ?type)
-  }
-  NOT EXISTS {
-    ?more_concrete_link_type rdfs:subClassOf <${link_type}> .
-    <${iri}> a ?more_concrete_link_type .
+  })
+  FILTER(NOT EXISTS {
+    ?more_concrete_link_type rdfs:subPropertyOf <${link_type}> .
+    ${reverse ? `?element ?more_concrete_link_type <${iri}>` : `<${iri}> ?more_concrete_link_type ?element`} . 
     FILTER(?more_concrete_link_type != <${link_type}>)
-  }
-}`;
+  })
+}`,
 
-// Query that gets the IRI and most-concrete-type of a given drawio cell.
-const getCellStuff = (cellId) => `\
+  // Query that gets the IRI and most-concrete-type of a given drawio cell.
+  getCellStuff: (cellId) => `\
 PREFIX drawio: <http://ua.be/sdo2l/vocabulary/formalisms/drawio#>
 PREFIX object_diagram: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>
 PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
@@ -44,10 +47,10 @@ WHERE {
     ?element a ?more_concrete_type .
     FILTER(?more_concrete_type != ?type)
   }
-}`;
+}`,
 
-// Query that, for a given cell IRI, gets the name of the diagram containing the cell, and the ID of the cell.
-const getDiagramAndCellId = cellIri => `\
+  // Query that, for a given cell IRI, gets the name of the diagram containing the cell, and the ID of the cell.
+  getDiagramAndCellId: cellIri => `\
 PREFIX drawio: <http://ua.be/sdo2l/vocabulary/formalisms/drawio#>
 PREFIX object_diagram: <http://ua.be/sdo2l/vocabulary/formalisms/object_diagram#>
 
@@ -56,69 +59,88 @@ WHERE {
   <${cellIri}> drawio:hasDrawioId ?cellId .
   <${cellIri}> object_diagram:inModel ?model .
   ?model object_diagram:hasName ?diagramName .
-}`;
+}`,
 
-// Query that gets ALL subclass relations.
-const getSubClassRelations = `
+  // Query that gets ALL subclass relations.
+  getSubClassRelations: `
 PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
 
 SELECT DISTINCT ?subclass ?superclass
 WHERE {
   ?subclass rdfs:subClassOf ?superclass
-}`;
+}`,
+};
 
 // (Hardcoded) link types exposed to the user.
 const typeToLinkType = new Map([
-  ["formalisms/object_diagram#Object", [
+  ["object_diagram#Object", [
     {
-      query: "formalisms/cs_as#parsedAs",
-      name: "Is parsed as",
+      relation: "cs_as#parsedAs",
+      description: (element, type) => `Is parsed as ${type}`,
     },
     {
-      query: "formalisms/cs_as#renderedAs",
-      name: "Is rendered as",
+      relation: "cs_as#renderedAs",
+      description: (element, type) => `Is rendered as ${type}`,
     },
+    // Outcommented, because it's not an interesting relation:
+    // {
+    //   relation: "object_diagram#inModel",
+    //   description: (element, type) => `Is part of model ${type}`,
+    // },
     {
-      query: "formalisms/object_diagram#inModel",
-      name: "Is part of model",
+      relation: "traceability_model#traceLinkTo",
+      description: (element, type) => `Trace-link (outgoing) ${element}`,
     },
     {
-      query: "formalisms/traceability_model#traceLinkTo",
-      name: "Trace-link (outgoing)",
+      relation: "traceability_model#traceLinkFrom",
+      description: (element, type) => `Trace-link (incoming) ${element}`,
     },
+  ]],
+  ["drawio#Model", [
     {
-      query: "formalisms/traceability_model#traceLinkFrom",
-      name: "Trace-link (incoming)",
+      relation: "drawio#hasRootCell",
+      description: (element, type) => `Has root ${type}`,
     },
   ]],
-  ["formalisms/drawio#Model", [
+  ["pm#Activity", [
+    {
+      relation: "pm#isTransformation",
+      description: (element, type) => `Is typed by ${type}`,
+    },
     {
-      query: "formalisms/drawio#hasRootCell",
-      name: "Has root",
+      relation: "pm#hasPort",
+      description: (element, type) => `Has port ${type}`,
     },
   ]],
-  ["formalisms/pm#Activity", [
+  ["pm#element", [
     {
-      query: "formalisms/pm#isTransformation",
-      name: "Is typed by transformation",
+      relation: "processtraces#relatesTo",
+      description: (element, type) => `Enacted by ${element}`,
+      reverse: true,
     },
   ]],
-  ["formalisms/pm#Artifact", [
+  ["processtraces#element", [
     {
-      query: "formalisms/pm#hasType",
-      name: "Is typed by formalism",
+      relation: "processtraces#relatesTo",
+      description: (element, type) => `Enactment of ${element}`,
     },
   ]],
-  ["formalisms/ftg#Transformation", [
+  ["pm#Artifact", [
     {
-      query: "formalisms/pm#occursAsActivity",
-      name: "Occurs as activity",
+      relation: "pm#hasType",
+      description: (element, type) => `Is typed by ${type}`,
+    },
+  ]],
+  ["ftg#Transformation", [
+    {
+      relation: "pm#occursAsActivity",
+      description: (element, type) => `Occurs as activity ${element}`,
     }
   ]],
-  ["formalisms/ftg#Formalism", [
+  ["ftg#Formalism", [
     {
-      query: "formalisms/pm#occursAsArtifact",
-      name: "Occurs as artifact",
+      relation: "pm#occursAsArtifact",
+      description: (element, type) => `Occurs as artifact ${element}`,
     },
   ]],
 ]);
@@ -130,7 +152,7 @@ function getQueries(type) {
   const result = [].concat(
     typeToLinkType.get(dropVocabularyPrefix(type)) || [],
     ... (superclasses.get(type) || []).map(supertype => typeToLinkType.get(dropVocabularyPrefix(supertype)) || []));
-  // console.log("getQueries,type=",type,"superclasses=",superclasses.get(type),"result=",result);
+  console.log("getQueries,type=",type,"superclasses=",superclasses.get(type),"result=",result);
   return result;
 }
 
@@ -174,7 +196,7 @@ fetch(BACKEND+"/version")
 
   // Loads all 'rdfs:subClassOf' relations and stores them in a mapping.
   function loadSuperclasses() {
-    return executeSPARQL(getSubClassRelations)
+    return executeSPARQL(QUERIES.getSubClassRelations)
     .then(results => {
       for (const {subclass, superclass} of results) {
         const superclasslist = superclasses.get(subclass.value) || (() => {
@@ -189,6 +211,60 @@ fetch(BACKEND+"/version")
 
   loadSuperclasses();
 
+  // Override context menu when right-clicking somewhere in the diagram:
+  const oldFactoryMethod = ui.editor.graph.popupMenuHandler.factoryMethod;
+  ui.editor.graph.popupMenuHandler.factoryMethod = function(menu, cell, evt) {
+    const modelverseMenu = menu.addItem("ModelVerse", null, null);
+    menu.addSeparator();
+    oldFactoryMethod.apply(this, arguments);
+
+    const alreadyVisited = new Set();
+    const entry = (element, type) => element+':'+type;
+
+    function addMenuItem(element, type, maxRecursion, parentMenuItem, description) {
+      if (alreadyVisited.has(entry(element, type))) {
+        return; // don't go in circles
+      }
+      const shortType = dropVocabularyPrefix(type);
+      const menuItemText = description(dropArtifactPrefix(element), shortType);
+      let createdMenuItem;
+      // If the menu item is a (subtype of a) drawio cell, then clicking on it will take us to that cell in drawio.
+      if ((superclasses.get(type) || []).some(t => t.endsWith("drawio#Cell"))) {
+        createdMenuItem = menu.addItem(menuItemText, null, () => goto(element), parentMenuItem);
+      } else {
+        createdMenuItem = menu.addItem(menuItemText, null, null, parentMenuItem);
+      }
+      alreadyVisited.add(entry(element, type));
+      if (maxRecursion <= 0) {
+        return;
+      }
+      addSubmenuItems(element, type, createdMenuItem, maxRecursion);
+    }
+    function addSubmenuItems(element, type, parentMenuItem, maxRecursion) {
+      const queries = getQueries(type);
+      for (const {relation, description, reverse} of queries) {
+        executeSPARQL(QUERIES.getOutgoingLink(element, "http://ua.be/sdo2l/vocabulary/formalisms/"+relation, reverse))
+        .then(results => {
+          for (const {element, type} of results) {
+            addMenuItem(element.value, type.value, maxRecursion-1, parentMenuItem, description);
+          }
+        })
+      }
+    }
+
+    // Populate ModelVerse submenu asynchronously:
+    if (cell && cell.vertex) {
+      executeSPARQL(QUERIES.getCellStuff(cell.id))
+      .then(results => {
+        for (const {element, type} of results) {
+          alreadyVisited.add(entry(element.value, type.value));
+          addSubmenuItems(element.value, type.value, modelverseMenu, 4);
+        }
+      })
+    }
+  }
+
+
   function getSetting(prop) {
     const result = localStorage.getItem("dtdesign-"+prop);
     if (result !== null) {
@@ -291,7 +367,7 @@ fetch(BACKEND+"/version")
 
   // Given a cell IRI, open the diagram that contains the cell
   function goto(cellIri) {
-    executeSPARQL(getDiagramAndCellId(cellIri))
+    executeSPARQL(QUERIES.getDiagramAndCellId(cellIri))
     .then(([{diagramName, cellId}]) => {
       // drop the "_drawio" at the end:
       const actualDiagramName = diagramName.value.substring(0, diagramName.value.length-7);
@@ -307,50 +383,6 @@ fetch(BACKEND+"/version")
     })
   }
 
-  // Override context menu when right-clicking somewhere in the diagram:
-  const oldFactoryMethod = ui.editor.graph.popupMenuHandler.factoryMethod;
-  ui.editor.graph.popupMenuHandler.factoryMethod = function(menu, cell, evt) {
-    // We recursively construct a submenu where the user can navigate the onthology:
-    const modelverseMenu = menu.addItem("ModelVerse", null, null);
-    oldFactoryMethod.apply(this, arguments);
-    if (cell && cell.vertex) {
-      const alreadyVisited = new Set();
-      function buildMenu(element, type, maxRecursion, parentMenuItem, relName) {
-        if (alreadyVisited.has(element+'---'+type)) {
-          return; // don't go in circles
-        }
-        const shortType = dropVocabularyPrefix(type);
-        let createdMenuItem;
-        // If the menu item is a (subtype of a) drawio cell, then clicking on it will take us to that cell in drawio.
-        if ((superclasses.get(type) || []).some(t => t.endsWith("drawio#Cell"))) {
-          createdMenuItem = menu.addItem(relName + ' ' + shortType, null, () => goto(element), parentMenuItem);
-        } else {
-          createdMenuItem = menu.addItem(relName + ' ' + shortType, null, null, parentMenuItem);
-        }
-        alreadyVisited.add(element+'---'+type);
-        if (maxRecursion <= 0) {
-          return;
-        }
-        const queries = getQueries(type);
-        for (const {query, name} of queries) {
-          // console.log("RemDepth=",maxRecursion,"element=",element,"queries=",queries);
-          executeSPARQL(getOutgoingLink(element, "http://ua.be/sdo2l/vocabulary/"+query))
-          .then(results => {
-            for (const {element, type} of results) {
-              buildMenu(element.value, type.value, maxRecursion-1, createdMenuItem, name);
-            }
-          })
-        }
-      }
-      executeSPARQL(getCellStuff(cell.id))
-      .then(results => {
-        for (const {element, type} of results) {
-          buildMenu(element.value, type.value, 10, modelverseMenu, "Is a");
-        }
-      })
-    }
-  }
-
   const wndDiv = document.createElement('div');
     wndDiv.style.userSelect = 'none';
     wndDiv.style.height = '100%';