]> granicus.if.org Git - graphviz/commitdiff
Add json output.
authorEmden R. Gansner <erg@emdenrg.net>
Mon, 1 Feb 2016 22:27:10 +0000 (17:27 -0500)
committerEmden R. Gansner <erg@emdenrg.net>
Mon, 1 Feb 2016 22:27:10 +0000 (17:27 -0500)
lib/gvc/gvcint.h
lib/gvc/gvcontext.c
plugin/core/Makefile.am
plugin/core/gvplugin_core.c
plugin/core/gvrender_core_json.c [new file with mode: 0644]

index e4d1eec2ba052d5453c06bfd6ae81bf833542b1e..510819e94e64b0fe4b99e6d1a5fd003b8f77eeeb 100644 (file)
@@ -143,6 +143,9 @@ extern "C" {
        int fontrenaming;
     };
 
+extern GVC_t* gvCloneGVC (GVC_t *);
+extern void gvFreeCloneGVC (GVC_t *);
+
 #ifdef WIN32
 #define DIRSEP "\\"
 #else
index cdf42616c40337414450982fcbdf08b8e4a3b788..7600e14ae169a80fe140c6b72a57aaf7afaca3ee 100644 (file)
@@ -98,3 +98,22 @@ int gvFreeContext(GVC_t * gvc)
     free(gvc);
     return (graphviz_errors + agerrors());
 }
+
+GVC_t* gvCloneGVC (GVC_t * gvc0)
+{
+    GVC_t *gvc = zmalloc(sizeof(GVC_t));
+
+    gvc->common = gvc0->common;
+    memcpy (&gvc->apis, &gvc0->apis, sizeof(gvc->apis));
+    memcpy (&gvc->api, &gvc0->api, sizeof(gvc->api));
+    gvc->packages = gvc->packages;
+    
+    return gvc;
+}
+
+void gvFreeCloneGVC (GVC_t * gvc)
+{
+    gvjobs_delete(gvc);
+    free(gvc);
+}
+
index 9b920989202afa7d368239e5099e2a0d104c17a8..cccc9e7fb3b21173aef1ef4a17574d0d884a7af2 100644 (file)
@@ -25,6 +25,7 @@ endif
 libgvplugin_core_C_la_SOURCES = \
        gvplugin_core.c \
        gvrender_core_dot.c \
+       gvrender_core_json.c \
        gvrender_core_fig.c \
        gvrender_core_map.c \
        gvrender_core_ps.c \
index 705f849c45e1d5df0ff2db5353a1346d5b0ccf37..8dc7e65726f40d4ab9d831abe5ddaf0a15863e50 100644 (file)
@@ -18,6 +18,7 @@ extern gvplugin_installed_t gvdevice_fig_types[];
 extern gvplugin_installed_t gvdevice_map_types[];
 extern gvplugin_installed_t gvdevice_ps_types[];
 extern gvplugin_installed_t gvdevice_svg_types[];
+extern gvplugin_installed_t gvdevice_json_types[];
 extern gvplugin_installed_t gvdevice_tk_types[];
 extern gvplugin_installed_t gvdevice_vml_types[];
 extern gvplugin_installed_t gvdevice_pic_types[];
@@ -28,6 +29,7 @@ extern gvplugin_installed_t gvrender_fig_types[];
 extern gvplugin_installed_t gvrender_map_types[];
 extern gvplugin_installed_t gvrender_ps_types[];
 extern gvplugin_installed_t gvrender_svg_types[];
+extern gvplugin_installed_t gvrender_json_types[];
 extern gvplugin_installed_t gvrender_tk_types[];
 extern gvplugin_installed_t gvrender_vml_types[];
 extern gvplugin_installed_t gvrender_pic_types[];
@@ -44,6 +46,7 @@ static gvplugin_api_t apis[] = {
     {API_device, gvdevice_map_types},
     {API_device, gvdevice_ps_types},
     {API_device, gvdevice_svg_types},
+    {API_device, gvdevice_json_types},
     {API_device, gvdevice_tk_types},
     {API_device, gvdevice_vml_types},
     {API_device, gvdevice_pic_types},
@@ -54,6 +57,7 @@ static gvplugin_api_t apis[] = {
     {API_render, gvrender_map_types},
     {API_render, gvrender_ps_types},
     {API_render, gvrender_svg_types},
+    {API_render, gvrender_json_types},
     {API_render, gvrender_tk_types},
     {API_render, gvrender_vml_types},
     {API_render, gvrender_pic_types},
diff --git a/plugin/core/gvrender_core_json.c b/plugin/core/gvrender_core_json.c
new file mode 100644 (file)
index 0000000..16a18c9
--- /dev/null
@@ -0,0 +1,420 @@
+/* $Id$ $Revision$ */
+/* vim:set shiftwidth=4 ts=8: */
+
+/*************************************************************************
+ * Copyright (c) 2015 AT&T Intellectual Property 
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: See CVS logs. Details at http://www.graphviz.org/
+ *************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#include <io.h>
+#include "compat.h"
+#endif
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "macros.h"
+#include "const.h"
+
+#include "gvplugin_render.h"
+#include "gvplugin_device.h"
+#include "agxbuf.h"
+#include "utils.h"
+#include "gvc.h"
+#include "gvio.h"
+#include "gvcint.h"
+
+typedef enum {
+       FORMAT_JSON,
+} format_type;
+
+typedef struct {
+    int Level;
+    int isLatin;
+    unsigned char Attrs_not_written_flag;
+} state_t;
+
+typedef struct {
+    Agrec_t h;
+    int id;
+} gid_t;
+
+#define ID "id"
+#define ND_gid(n) (((gid_t*)aggetrec(n, ID, FALSE))->id) 
+#define ED_gid(n) (((gid_t*)aggetrec(n, ID, FALSE))->id) 
+
+static void json_begin_graph(GVJ_t *job)
+{
+    GVC_t* gvc = gvCloneGVC (job->gvc); 
+    graph_t *g = job->obj->u.g;
+    gvRender (gvc, g, "xdot", NULL); 
+    gvFreeCloneGVC (gvc);
+}
+
+#define LOCALNAMEPREFIX                '%'
+
+/* stoj:
+ * Convert dot string to a valid json string embedded in double quotes.
+ */
+static char* stoj (char* ins, state_t* sp)
+{
+    char* s;
+    char* input;
+    static agxbuf xb;
+    unsigned char c;
+
+    if (sp->isLatin)
+       input = latin1ToUTF8 (ins);
+    else
+       input = ins;
+
+    if (xb.buf == NULL)
+       agxbinit(&xb, BUFSIZ, NULL);
+    for (s = input; (c = *s); s++) {
+       switch (c) {
+       case '"' :
+           agxbput(&xb, "\\\"");
+           break;
+       case '\\' :
+           agxbput(&xb, "\\\\");
+           break;
+       case '/' :
+           agxbput(&xb, "\\/");
+           break;
+       case '\b' :
+           agxbput(&xb, "\\b");
+           break;
+       case '\f' :
+           agxbput(&xb, "\\f");
+           break;
+       case '\n' :
+           agxbput(&xb, "\\n");
+           break;
+       case '\r' :
+           agxbput(&xb, "\\r");
+           break;
+       case '\t' :
+           agxbput(&xb, "\\t");
+           break;
+       default :
+           agxbputc(&xb, c);
+           break;
+       }
+    }
+    s = agxbuse(&xb);
+
+    if (sp->isLatin)
+       free (input);
+    return s;
+}
+
+static void indent(GVJ_t * job, int level)
+{
+    int i;
+    for (i = level; i > 0; i--)
+       gvputs(job, "  ");
+}
+
+static void set_attrwf(Agraph_t * g, int toplevel, int value)
+{
+    Agraph_t *subg;
+    Agnode_t *n;
+    Agedge_t *e;
+
+    AGATTRWF(g) = value;
+    for (subg = agfstsubg(g); subg; subg = agnxtsubg(subg)) {
+       set_attrwf(subg, FALSE, value);
+    }
+    if (toplevel) {
+       for (n = agfstnode(g); n; n = agnxtnode(g, n)) {
+           AGATTRWF(n) = value;
+           for (e = agfstout(g, n); e; e = agnxtout(g, e))
+               AGATTRWF(e) = value;
+       }
+    }
+}
+
+static void write_attrs(Agobj_t * obj, GVJ_t * job, state_t* sp)
+{
+    Agraph_t* g = agroot(obj);
+    int type = AGTYPE(obj);
+    Agsym_t* sym = agnxtattr(g, type, NULL);
+    if (!sym) return;
+
+    for (; sym; sym = agnxtattr(g, type, sym)) {
+       gvputs(job, ",\n");
+       indent (job, sp->Level);
+       gvprintf(job, "\"%s\" : ", stoj(sym->name, sp));
+       gvprintf(job, "\"%s\"", stoj(agxget(obj, sym), sp));
+    }
+}
+
+static void write_hdr(Agraph_t * g, GVJ_t * job, int top, state_t* sp)
+{
+    char *name;
+
+    name = agnameof(g);
+    indent (job, sp->Level);
+    gvprintf(job, "\"name\" : \"%s\"", stoj (name, sp));
+
+    if (top) {
+       gvputs(job, ",\n");
+       indent (job, sp->Level);
+       gvprintf(job, "\"directed\" : %s,\n", (agisdirected(g)?"true":"false"));
+       indent (job, sp->Level);
+       gvprintf(job, "\"strict\" : %s", (agisstrict(g)?"true":"false"));
+    }
+}
+
+static void write_graph(Agraph_t * g, GVJ_t * job, int top, state_t* sp);
+
+static int write_subgs(Agraph_t * g, GVJ_t * job, state_t* sp)
+{
+    Agraph_t* sg;
+    int not_first = 0;
+
+    sg = agfstsubg(g);
+    if (!sg) return 0;
+   
+    gvputs(job, ",\n");
+    indent (job, sp->Level++);
+    gvputs(job, "\"subgraphs\" : [\n");
+    for (; sg; sg = agnxtsubg(sg)) {
+       if (not_first) 
+           gvputs(job, ",\n");
+       else
+           not_first = 1;
+       write_graph (sg, job, FALSE, sp);
+    }
+    sp->Level--;
+    gvputs(job, "\n");
+    indent (job, sp->Level);
+    gvputs(job, "]");
+    return 1;
+}
+
+static void write_edge(Agedge_t * e, GVJ_t * job, int top, state_t* sp)
+{
+    if (top) {
+       indent (job, sp->Level++);
+       gvputs(job, "{\n");
+       indent (job, sp->Level);
+       gvprintf(job, "\"id\" : %d,\n", ED_gid(e));
+       indent (job, sp->Level);
+       gvprintf(job, "\"tail\" : %d,\n", ND_gid(agtail(e)));
+       indent (job, sp->Level);
+       gvprintf(job, "\"head\" : %d", ND_gid(aghead(e)));
+       write_attrs((Agobj_t*)e, job, sp);
+       gvputs(job, "\n");
+       sp->Level--;
+       indent (job, sp->Level);
+       gvputs(job, "}");
+    }
+    else {
+       indent (job, sp->Level++);
+       gvprintf(job, "%d", ED_gid(e));
+       sp->Level--;
+    }
+}
+
+static int write_edges(Agraph_t * g, GVJ_t * job, int top, state_t* sp)
+{
+    Agnode_t* np;
+    Agedge_t* ep;
+    int not_first = 0;
+
+    np = agfstnode(g);
+    if (!np) return 0;
+    ep = agfstout(g, np);
+    if (!ep) return 0;
+    gvputs(job, ",\n");
+    indent (job, sp->Level++);
+    gvputs(job, "\"edges\" : [\n");
+    for (; np; np = agnxtnode(g,np)) {
+       for (ep = agfstout(g, np); ep; ep = agnxtout(g,ep)) {
+           if (not_first) 
+               gvputs(job, ",\n");
+           else
+               not_first = 1;
+           write_edge(ep, job, top, sp);
+       }
+    }
+    sp->Level--;
+    gvputs(job, "\n");
+    indent (job, sp->Level);
+    gvputs(job, "]");
+    return 1;
+}
+
+static void write_node(Agnode_t * n, GVJ_t * job, int top, state_t* sp)
+{
+    indent (job, sp->Level++);
+    if (top) {
+       gvputs(job, "{\n");
+       indent (job, sp->Level);
+       gvprintf(job, "\"id\" : %d,\n", ND_gid(n));
+       indent (job, sp->Level);
+       gvprintf(job, "\"name\" : \"%s\"", stoj (agnameof(n), sp));
+       write_attrs((Agobj_t*)n, job, sp);
+       gvputs(job, "\n");
+       sp->Level--;
+       indent (job, sp->Level);
+       gvputs(job, "}");
+    }
+    else {
+       gvprintf(job, "%d", ND_gid(n));
+       sp->Level--;
+    }
+}
+
+static int write_nodes(Agraph_t * g, GVJ_t * job, int top, state_t* sp)
+{
+    Agnode_t* n;
+    int not_first = 0;
+
+    n = agfstnode(g);
+    if (!n) return 0;
+    gvputs(job, ",\n");
+    indent (job, sp->Level++);
+    gvputs(job, "\"nodes\" : [\n");
+    for (; n; n = agnxtnode(g, n)) {
+       if (not_first) 
+           gvputs(job, ",\n");
+       else
+           not_first = 1;
+       write_node (n, job, top, sp);
+    }
+    sp->Level--;
+    gvputs(job, "\n");
+    indent (job, sp->Level);
+    gvputs(job, "]");
+    return 1;
+}
+
+static void write_graph(Agraph_t * g, GVJ_t * job, int top, state_t* sp)
+{
+    Agnode_t* np; 
+    Agedge_t* ep; 
+    int ncnt = 0;
+    int ecnt = 0;
+
+    if (top) {
+       aginit(g, AGNODE, ID, sizeof(gid_t), FALSE);
+       aginit(g, AGEDGE, ID, sizeof(gid_t), FALSE);
+       for (np = agfstnode(g); np; np = agnxtnode(g,np)) {
+           ND_gid(np) = ncnt++;
+           for (ep = agfstout(g, np); ep; ep = agnxtout(g,ep)) {
+               ED_gid(ep) = ecnt++;
+           }
+       }
+    }
+
+    indent (job, sp->Level++);
+    gvputs(job, "{\n");
+    write_hdr(g, job, top, sp);
+    write_attrs((Agobj_t*)g, job, sp);
+    write_subgs(g, job, sp);
+    write_nodes (g, job, top, sp);
+    write_edges (g, job, top, sp);
+    gvputs(job, "\n");
+    sp->Level--;
+    indent (job, sp->Level);
+    gvputs(job, "}");
+}
+
+typedef int (*putstrfn) (void *chan, const char *str);
+typedef int (*flushfn) (void *chan);
+
+static void json_end_graph(GVJ_t *job)
+{
+    graph_t *g = job->obj->u.g;
+    state_t sp;
+    Agiodisc_t* io_save;
+    static Agiodisc_t io;
+
+    if (io.afread == NULL) {
+       io.afread = AgIoDisc.afread;
+       io.putstr = (putstrfn)gvputs;
+       io.flush = (flushfn)gvflush;
+    }
+
+    io_save = g->clos->disc.io;
+    g->clos->disc.io = &io;
+
+    set_attrwf(g, TRUE, FALSE);
+    sp.Level = 0;
+    sp.isLatin = (GD_charset(g) == CHAR_LATIN1);
+    sp.Attrs_not_written_flag = 0;
+    write_graph(g, job, TRUE, &sp);
+    /* agwrite(g, (FILE*)job); */
+}
+
+gvrender_engine_t json_engine = {
+    0,                         /* json_begin_job */
+    0,                         /* json_end_job */
+    json_begin_graph,
+    json_end_graph,
+    0,                         /* json_begin_layer */
+    0,                         /* json_end_layer */
+    0,                         /* json_begin_page */
+    0,                         /* json_end_page */
+    0,                         /* json_begin_cluster */
+    0,                         /* json_end_cluster */
+    0,                         /* json_begin_nodes */
+    0,                         /* json_end_nodes */
+    0,                         /* json_begin_edges */
+    0,                         /* json_end_edges */
+    0,                         /* json_begin_node */
+    0,                         /* json_end_node */
+    0,                         /* json_begin_edge */
+    0,                         /* json_end_edge */
+    0,                         /* json_begin_anchor */
+    0,                         /* json_end_anchor */
+    0,                         /* json_begin_label */
+    0,                         /* json_end_label */
+    0,                         /* json_textspan */
+    0,                         /* json_resolve_color */
+    0,                         /* json_ellipse */
+    0,                         /* json_polygon */
+    0,                         /* json_bezier */
+    0,                         /* json_polyline */
+    0,                         /* json_comment */
+    0,                         /* json_library_shape */
+};
+
+gvrender_features_t render_features_json = {
+    GVRENDER_DOES_TRANSFORM,   /* not really - uses raw graph coords */  /* flags */
+    0.,                         /* default pad - graph units */
+    NULL,                      /* knowncolors */
+    0,                         /* sizeof knowncolors */
+    COLOR_STRING,              /* color_type */
+};
+
+gvdevice_features_t device_features_json = {
+    0,                         /* flags */
+    {0.,0.},                   /* default margin - points */
+    {0.,0.},                   /* default page width, height - points */
+    {72.,72.},                 /* default dpi */
+};
+
+gvplugin_installed_t gvrender_json_types[] = {
+    {FORMAT_JSON, "json", 1, &json_engine, &render_features_json},
+    {0, NULL, 0, NULL, NULL}
+};
+
+gvplugin_installed_t gvdevice_json_types[] = {
+    {FORMAT_JSON, "json:json", 1, NULL, &device_features_json},
+    {0, NULL, 0, NULL, NULL}
+};