X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=parser.js;h=0d52ee00005bb6889b298d40ee312ab0c68a4956;hb=4ef6cdeda1d92848df61a92f8fa4af20761b99bb;hp=8a4c52499f17df2fe92e28150b72e825b98c0f69;hpb=d6c9499e06c65780d5f4c95664c2cb851ff6f2de;p=erdiag.git diff --git a/parser.js b/parser.js index 8a4c524..0d52ee0 100644 --- a/parser.js +++ b/parser.js @@ -3,11 +3,10 @@ class ErDiags { constructor(description) { - this.entities = {}; - this.inheritances = []; - this.associations = []; + this.entities = { }; + this.inheritances = [ ]; + this.associations = [ ]; this.txt2json(description); - this.tables = []; // Cache SVG graphs returned by server (in addition to server cache = good perfs) this.mcdGraph = ""; this.mldGraph = ""; @@ -26,7 +25,9 @@ class ErDiags "*": "0,n", "+": "1,n", "?": "0,1", - "1": "1,1" + "1": "1,1", + "?R": "(0,1)", + "1R": "(1,1)", }; } @@ -67,7 +68,7 @@ class ErDiags { case '[': // Entity = { name: { attributes, [weak] } } - let name = lines[start].match(/[^\[\]\s]+/)[0]; + let name = lines[start].match(/[^\[\]"\s]+/)[0]; let entity = { attributes: this.parseAttributes(lines, start+1, end) }; if (lines[start].charAt(1) == '[') entity.weak = true; @@ -79,7 +80,7 @@ class ErDiags case '{': //association // Association = { [name], [attributes], [weak], entities: ArrayOf entity indices } let relationship = { }; - let nameRes = lines[start].match(/[^{}\s]+/); + let nameRes = lines[start].match(/[^{}"\s]+/); if (nameRes !== null) relationship.name = nameRes[0]; if (lines[start].charAt(1) == '{') @@ -102,7 +103,7 @@ class ErDiags field.isKey = true; line = line.slice(1); } - field.name = line.match(/[^()\s]+/)[0]; + field.name = line.match(/[^()"\s]+/)[0]; let parenthesis = line.match(/\((.+)\)/); if (parenthesis !== null) { @@ -136,7 +137,10 @@ class ErDiags return inheritance; } - // Association (parsed here): { entities: ArrayOf entity names + cardinality, [attributes: ArrayOf {name, [isKey], [type], [qualifiers]}] } + // Association (parsed here): { + // entities: ArrayOf entity names + cardinality, + // [attributes: ArrayOf {name, [isKey], [type], [qualifiers]}] + // } parseAssociation(lines, start, end) { let assoce = { }; @@ -177,6 +181,7 @@ class ErDiags } // "Modèle conceptuel des données". TODO: option for graph size + // NOTE: randomizing helps to obtain better graphs (sometimes) drawMcd(id, mcdStyle) //mcdStyle: bubble, or compact { let element = document.getElementById(id); @@ -188,11 +193,14 @@ class ErDiags } // Build dot graph input let mcdDot = 'graph {\n'; + mcdDot += 'rankdir="LR";\n'; // Nodes: - Object.keys(this.entities).forEach( name => { + if (mcdStyle == "compact") + mcdDot += 'node [shape=plaintext];\n'; + _.shuffle(Object.keys(this.entities)).forEach( name => { if (mcdStyle == "bubble") { - mcdDot += name + '[shape=rectangle, label="' + name + '"'; + mcdDot += '"' + name + '" [shape=rectangle, label="' + name + '"'; if (this.entities[name].weak) mcdDot += ', peripheries=2'; mcdDot += '];\n'; @@ -200,14 +208,18 @@ class ErDiags { this.entities[name].attributes.forEach( a => { let label = (a.isKey ? '#' : '') + a.name; - mcdDot += name + '_' + a.name + '[shape=ellipse, label="' + label + '"];\n'; - mcdDot += name + '_' + a.name + ' -- ' + name + ';\n'; + let attrName = name + '_' + a.name; + mcdDot += '"' + attrName + '" [shape=ellipse, label="' + label + '"];\n'; + if (Math.random() < 0.5) + mcdDot += '"' + attrName + '" -- "' + name + '";\n'; + else + mcdDot += '"' + name + '" -- "' + attrName + '";\n'; }); } } else { - mcdDot += name + '[shape=plaintext, label=<'; + mcdDot += '"' + name + '" [label=<'; if (this.entities[name].weak) { mcdDot += '
) drawMld(id) { let element = document.getElementById(id); @@ -274,18 +323,21 @@ class ErDiags element.innerHTML = this.mcdGraph; return; } - // Build dot graph input + // Build dot graph input (assuming foreign keys not already present...) let mldDot = 'graph {\n'; - // Nodes: + // Pass 1: initialize tables + let tables = [ ]; Object.keys(this.entities).forEach( name => { - //mld. ... --> devient table - // mldDot = ... + tables.push({ name: this.entities[name] }); //TODO: should be a (deep) copy }); - // Relationships: + // Pass 2: parse associations, add foreign keys + new tables this.associations.forEach( a => { a.entities.forEach( e => { // e.card e.name ... - // Pass 1 : entites deviennent tables - // Pass 2 : sur les assoces + switch (e.card) + { + case '?': + case '?R': //"weak tables" foreign keys become part of the key + // TODO // multi-arite : sub-loop si 0,1 ou 1,1 : aspiré comme attribut de l'association (phase 1) // ensuite, que du 0,n ou 1,n : si == 1, OK une table // si 2 ou + : n tables + 1 pour l'assoce, avec attrs clés étrangères |