diff --git a/CHANGELOG.md b/CHANGELOG.md index 853e75370c1de7a5ed5145ecccf295b81f9ce4e7..cdcd28771ef2558efdaad66eab9fdf3a5541a2b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - C++11 support is now required of the C++ compiler used to build Graphviz - C99 support is now required of the C compiler used to build Graphviz - Question about userout() function in agerror.c #1924 +- The edges in JSON output are ordered now !1728 ### Fixed - memory leak in label construction diff --git a/plugin/core/gvrender_core_json.c b/plugin/core/gvrender_core_json.c index 1b5a998aa3b65a13bf15990b5550f01478a1de98..5e9a1d398b536ac93ceeabeda81a3635ce00056c 100644 --- a/plugin/core/gvrender_core_json.c +++ b/plugin/core/gvrender_core_json.c @@ -33,6 +33,8 @@ #include #include +#include + typedef enum { FORMAT_JSON, FORMAT_JSON0, @@ -460,6 +462,22 @@ static int write_subgs(Agraph_t * g, GVJ_t * job, int top, state_t* sp) return 1; } +static int agseqasc(Agedge_t **lhs, Agedge_t **rhs) +{ + Agedge_t *e1 = *lhs; + Agedge_t *e2 = *rhs; + + if (AGSEQ(e1) < AGSEQ(e2)) { + return -1; + } + else if (AGSEQ(e1) > AGSEQ(e2)) { + return 1; + } + else { + return 0; + } +} + static void write_edge(Agedge_t * e, GVJ_t * job, int top, state_t* sp) { if (top) { @@ -484,39 +502,49 @@ static void write_edge(Agedge_t * e, GVJ_t * job, int top, state_t* sp) static int write_edges(Agraph_t * g, GVJ_t * job, int top, state_t* sp) { - Agnode_t* np; - Agedge_t* ep; + size_t count = 0; int not_first = 0; - np = agfstnode(g); - if (!np) return 0; - ep = NULL; - /* find a first edge */ - for (; np; np = agnxtnode(g,np)) { - for (ep = agfstout(g, np); ep; ep = agnxtout(g,ep)) { - if (ep) break; - } - if (ep) break; + for (Agnode_t *np = agfstnode(g); np; np = agnxtnode(g, np)) { + for (Agedge_t *ep = agfstout(g, np); ep; ep = agnxtout(g, ep)) { + ++count; + } } - if (!ep) return 0; + + if (count == 0) { + return 0; + } + + Agedge_t **edges = gcalloc(count, sizeof(Agedge_t *)); + + size_t i = 0; + for (Agnode_t *np = agfstnode(g); np; np = agnxtnode(g, np)) { + for (Agedge_t *ep = agfstout(g, np); ep; ep = agnxtout(g, ep)) { + edges[i] = ep; + ++i; + } + } + + qsort(edges, count, sizeof(Agedge_t *), (qsort_cmpf)agseqasc); gvputs(job, ",\n"); indent(job, sp->Level++); gvputs(job, "\"edges\": [\n"); if (!top) indent(job, sp->Level); - for (; np; np = agnxtnode(g,np)) { - for (ep = agfstout(g, np); ep; ep = agnxtout(g,ep)) { - if (not_first) - if (top) - gvputs(job, ",\n"); - else - gvputs(job, ","); - else - not_first = 1; - write_edge(ep, job, top, sp); - } + for (size_t j = 0; j < count; ++j) { + if (not_first) + if (top) + gvputs(job, ",\n"); + else + gvputs(job, ","); + else + not_first = 1; + write_edge(edges[j], job, top, sp); } + + free(edges); + sp->Level--; gvputs(job, "\n"); indent(job, sp->Level);