diff --git a/lib/network/dotparser.js b/lib/network/dotparser.js index 6e38c9e8..ee3f1997 100644 --- a/lib/network/dotparser.js +++ b/lib/network/dotparser.js @@ -10,6 +10,29 @@ * @return {Object} graph An object containing two parameters: * {Object[]} nodes * {Object[]} edges + * + * ------------------------------------------- + * TODO + * ==== + * + * For label handling, this is an incomplete implementation. From docs (quote #3015): + * + * > the escape sequences "\n", "\l" and "\r" divide the label into lines, centered, + * > left-justified, and right-justified, respectively. + * + * Source: http://www.graphviz.org/content/attrs#kescString + * + * > As another aid for readability, dot allows double-quoted strings to span multiple physical + * > lines using the standard C convention of a backslash immediately preceding a newline + * > character + * > In addition, double-quoted strings can be concatenated using a '+' operator. + * > As HTML strings can contain newline characters, which are used solely for formatting, + * > the language does not allow escaped newlines or concatenation operators to be used + * > within them. + * + * - Currently, only '\\n' is handled + * - Note that text explicitly says 'labels'; the dot parser currently handles escape + * sequences in **all** strings. */ function parseDOT (data) { dot = data; @@ -358,9 +381,14 @@ function getToken() { if (c === '"') { next(); while (c != '' && (c != '"' || (c === '"' && nextPreview() === '"'))) { - token += c; - if (c === '"') { // skip the escape character + if (c === '"') { // skip the escape character + token += c; + next(); + } else if (c === '\\' && nextPreview() === 'n') { // Honor a newline escape sequence + token += '\n'; next(); + } else { + token += c; } next(); } diff --git a/test/dotparser.test.js b/test/dotparser.test.js index da951f26..c9c6412b 100644 --- a/test/dotparser.test.js +++ b/test/dotparser.test.js @@ -183,4 +183,78 @@ describe('dotparser', function () { }); }); + + /** + * DOT-format examples taken from #3015 + */ + it('properly handles newline escape sequences in strings', function (done) { + var data = 'dinetwork {1 [label="new\\nline"];}'; + + data = String(data); + + var graph = dot.parseDOT(data); + + assert.deepEqual(graph, { + "id": "dinetwork", + "nodes": [ + { + "id": 1, + "attr": { + "label": "new\nline", // And not "new\\nline" + } + } + ] + }); + + + // Note the double backslashes + var data2 = 'digraph {' + "\n" + +' 3 [color="#0d2b7c", label="query:1230:add_q\\n0.005283\\n6.83%\\n(0.0001)\\n(0.13%)\\n17×"];' + "\n" + +' 3 -> 7 [color="#0d2a7b", fontcolor="#0d2a7b", label="0.005128\\n6.63%\\n17×"];' + "\n" + +' 5 [color="#0d1976", label="urlresolvers:537:reverse\\n0.00219\\n2.83%\\n(0.000193)\\n(0.25%)\\n29×"];' + "\n" + +"}" + + data2 = String(data2); + + var graph2 = dot.parseDOT(data2); + //console.log(JSON.stringify(graph, null, 2)); + + assert.deepEqual(graph2, { + "type": "digraph", + "nodes": [ + { + "id": 3, + "attr": { + "color": "#0d2b7c", + "label": "query:1230:add_q\n0.005283\n6.83%\n(0.0001)\n(0.13%)\n17×" + } + }, + { + "id": 7 + }, + { + "id": 5, + "attr": { + "color": "#0d1976", + "label": "urlresolvers:537:reverse\n0.00219\n2.83%\n(0.000193)\n(0.25%)\n29×" + } + } + ], + "edges": [ + { + "from": 3, + "to": 7, + "type": "->", + "attr": { + "color": "#0d2a7b", + "fontcolor": "#0d2a7b", + "label": "0.005128\n6.63%\n17×" + } + } + ] + }); + + done(); + }); + });