123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244(**************************************************************************)(* *)(* This file is part of OcamlGraph. *)(* *)(* Copyright (C) 2009-2010 *)(* CEA (Commissariat � l'�nergie Atomique) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1, with a linking exception. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the file ../LICENSE for more details. *)(* *)(* Authors: *)(* - Julien Signoles (Julien.Signoles@cea.fr) *)(* - Jean-Denis Koeck (jdkoeck@gmail.com) *)(* - Benoit Bataille (benoit.bataille@gmail.com) *)(* *)(**************************************************************************)(* This graph model is for now immutable, no adding or removing nodes. *)openXDotexceptionDotErrorofstring(* ABSTRACT CLASS *)classtype['vertex,'edge,'cluster]abstract_model=objectmethoditer_edges:('vertex->'vertex->unit)->unitmethoditer_edges_e:('edge->unit)->unitmethoditer_pred:('vertex->unit)->'vertex->unitmethoditer_pred_e:('edge->unit)->'vertex->unitmethoditer_succ:('vertex->unit)->'vertex->unitmethoditer_succ_e:('edge->unit)->'vertex->unitmethoditer_vertex:('vertex->unit)->unitmethoditer_clusters:('cluster->unit)->unitmethoditer_associated_vertex:('vertex->unit)->'vertex->unit(** Membership functions *)methodfind_edge:'vertex->'vertex->'edgemethodmem_edge:'vertex->'vertex->boolmethodmem_edge_e:'edge->boolmethodmem_vertex:'vertex->boolmethodsrc:'edge->'vertexmethoddst:'edge->'vertex(** Dot layout *)methodbounding_box:bounding_boxmethodget_edge_layout:'edge->edge_layoutmethodget_vertex_layout:'vertex->node_layoutmethodget_cluster_layout:'cluster->cluster_layoutend(* BUILDING A MODEL WITH AN OCAML GRAPH *)moduleMake(G:Graphviz.GraphWithDotAttrs)=structexceptionMultiple_layoutsof(G.E.t*edge_layout)listtypecluster=stringmoduleX=XDot.Make(G)classmodellayoutg:[G.vertex,G.edge,cluster]abstract_model=object(self)(* Iterators *)methoditer_edgesf=G.iter_edgesfgmethoditer_edges_ef=G.iter_edges_efgmethoditer_predfv=G.iter_predfgvmethoditer_pred_efv=G.iter_pred_efgvmethoditer_succf=G.iter_succfgmethoditer_succ_ef=G.iter_succ_efgmethoditer_vertexf=G.iter_vertexfgmethoditer_associated_vertexfv=fvmethoditer_clustersf=Hashtbl.iter(funk_->fk)layout.X.cluster_layouts(* Membership functions *)methodfind_edge=tryG.find_edgegwithNot_found->assertfalsemethodmem_edge=G.mem_edgegmethodmem_edge_e=G.mem_edge_egmethodmem_vertex=G.mem_vertexgmethodsrc=G.E.srcmethoddst=G.E.dst(* Layout *)methodbounding_box=layout.X.bboxmethodget_vertex_layoutv=tryX.HV.findlayout.X.vertex_layoutsvwithNot_found->assertfalsemethodget_edge_layoute=tryX.HE.findelayout.X.edge_layoutswithNot_found->(* if there are several edges from a vertex [v1] to a vertex [v2], they
can share the same layout. In that case, one these edges is
unfortunately not in the layout table because of key sharing. Try to
recover it when possible by creating a list of all possible layouts
for the given edge. If there is only one, easy win, otherwise return
them all in an exception and let the caller decide what to do *)letlayouts=ref[]inself#iter_succ_e(fune'->ifG.V.equal(self#dste)(self#dste')thentryletlayout=X.HE.finde'layout.X.edge_layoutsinifnot(List.exists(fun(_,l)->layout=l)!layouts)thenlayouts:=(e',layout)::!layoutswithNot_found->())(self#srce);match!layoutswith|[]->assertfalse|[_,x]->x|_::_::_->raise(Multiple_layouts!layouts)methodget_cluster_layoutc=tryHashtbl.findlayout.X.cluster_layoutscwithNot_found->assertfalseendletfrom_graph?(cmd="dot")?(tmp_name="dgraph")g=(* Output dot file *)letmoduleDumpDot=Graphviz.Dot(G)inletdot_file,out=Filename.open_temp_filetmp_name".dot"inDumpDot.output_graphoutg;close_outout;(* Get layout from dot file *)letlayout=tryX.layout_of_dot~cmd~dot_filegwithX.DotErrorerr->raise(DotErrorerr)inletmodel=newmodellayoutginSys.removedot_file;modelend(* BUILDING A MODEL WITH A DOT FILE *)(* Here we build a model from a graph where vertices and edges
are labeled with xdot layouts *)moduleVertex=structtypet=XDot.node_layoutendmoduleEdge=structtypet=XDot.edge_layoutletdefault=XDot.mk_edge_layout~draw:[]~ldraw:[]~hdraw:[]~tdraw:[]~hldraw:[]~tldraw:[]letcompare:t->t->int=Stdlib.compareendmoduleDotG=Imperative.Digraph.AbstractLabeled(Vertex)(Edge)moduleDotB=Builder.I(DotG)typecluster=stringtypedotg_model=(DotG.vertex,DotG.edge,cluster)abstract_modelmoduleDotParser=Dot.Parse(DotB)(struct(* Read the attributes of a node *)letnode=XDot.read_node_layout(* Read edge attributes *)letedge=XDot.read_edge_layoutend)moduleDotModel=structtypecluster=stringclassmodelgclusters_hashbounding_box:[DotG.vertex,DotG.edge,cluster]abstract_model=object(* Iterators *)methoditer_edgesf=DotG.iter_edgesfgmethoditer_edges_ef=DotG.iter_edges_efgmethoditer_predfv=DotG.iter_predfgvmethoditer_pred_efv=DotG.iter_pred_efgvmethoditer_succf=DotG.iter_succfgmethoditer_succ_ef=DotG.iter_succ_efgmethoditer_vertexf=DotG.iter_vertexfgmethoditer_associated_vertexfv=fvmethoditer_clustersf=Hashtbl.iter(funk_->fk)clusters_hash(* Membership functions *)methodfind_edge=tryDotG.find_edgegwithNot_found->assertfalsemethodmem_edge=DotG.mem_edgegmethodmem_edge_e=DotG.mem_edge_egmethodmem_vertex=DotG.mem_vertexgmethodsrc=DotG.E.srcmethoddst=DotG.E.dst(* Layout *)methodbounding_box=bounding_boxmethodget_vertex_layout=DotG.V.labelmethodget_edge_layout=DotG.E.labelmethodget_cluster_layoutc=letattrs=tryHashtbl.findclusters_hashcwithNot_found->assertfalseinXDot.read_cluster_layoutattrsendletmodel=newmodelend(* Runs graphviz, parses the graph and instantiates the model *)letread_dot?(cmd="dot")dot_file=letbasename=tryFilename.chop_extensiondot_filewithInvalid_argument_->dot_fileinletxdot_file=basename^".xdot"inletdot_cmd=Printf.sprintf"%s -Txdot %s > %s"cmddot_filexdot_filein(* Run graphviz *)matchSys.commanddot_cmdwith|0->beginletgraph,bb,clusters_hash=DotParser.parse_bounding_box_and_clustersxdot_fileinDotModel.modelgraphclusters_hash(XDot.read_bounding_boxbb)end|_->raise(DotError"Error during dot execution")(* Does not run graphviz.
Parses a graph from an xdot file and instantiates the model. *)letread_xdotxdot_file=letgraph,bb,clusters_hash=DotParser.parse_bounding_box_and_clustersxdot_fileinDotModel.modelgraphclusters_hash(XDot.read_bounding_boxbb)