diff --git a/Project.toml b/Project.toml index 46535a3..da7d802 100644 --- a/Project.toml +++ b/Project.toml @@ -13,10 +13,8 @@ EzXML = "8f5d6c58-4d21-5cfd-889c-e3ad7ee6a615" ParserCombinator = "fae87a5f-d1ad-5cf0-8f61-c941e1580b46" [extensions] -GraphIODOTExt = "ParserCombinator" -GraphIOGEXFExt = "EzXML" -GraphIOGMLExt = "ParserCombinator" -GraphIOGraphMLExt = "EzXML" +GraphIOParserCombinator = "ParserCombinator" +GraphIOXML = "EzXML" GraphIOLGCompressedExt = "CodecZlib" [compat] diff --git a/ext/GraphIODOTExt.jl b/ext/GraphIODOTExt.jl deleted file mode 100644 index f424e9d..0000000 --- a/ext/GraphIODOTExt.jl +++ /dev/null @@ -1,95 +0,0 @@ -module GraphIODOTExt - -using Graphs -import Graphs: loadgraph, loadgraphs, savegraph - -@static if isdefined(Base, :get_extension) - using GraphIO - using ParserCombinator - import GraphIO.DOT.DOTFormat -else # not required for julia >= v1.9 - using ..GraphIO - using ..ParserCombinator - import ..GraphIO.DOT.DOTFormat -end - -function savedot(io::IO, g::AbstractGraph, gname::String="") - isdir = is_directed(g) - println(io, (isdir ? "digraph " : "graph ") * gname * " {") - for i in vertices(g) - println(io, "\t" * string(i)) - end - if isdir - for u in vertices(g) - out_nbrs = outneighbors(g, u) - length(out_nbrs) == 0 && continue - println(io, "\t" * string(u) * " -> {" * join(out_nbrs, ',') * "}") - end - else - for e in edges(g) - source = string(src(e)) - dest = string(dst(e)) - println(io, "\t" * source * " -- " * dest) - end - end - println(io, "}") - return 1 -end - -function savedot_mult(io::IO, graphs::Dict) - ng = 0 - for (gname, g) in graphs - ng += savedot(io, g, gname) - end - return ng -end - -function _dot_read_one_graph(pg::Parsers.DOT.Graph) - isdir = pg.directed - nvg = length(Parsers.DOT.nodes(pg)) - nodedict = Dict(zip(collect(Parsers.DOT.nodes(pg)), 1:nvg)) - if isdir - g = DiGraph(nvg) - else - g = Graph(nvg) - end - for es in Parsers.DOT.edges(pg) - s = nodedict[es[1]] - d = nodedict[es[2]] - add_edge!(g, s, d) - end - return g -end - -function _name(pg::Parsers.DOT.Graph) - return if pg.id !== nothing - pg.id.id - else - Parsers.DOT.StringID(pg.directed ? "digraph" : "graph") - end -end - -function loaddot(io::IO, gname::String) - p = Parsers.DOT.parse_dot(read(io, String)) - for pg in p - _name(pg) == gname && return _dot_read_one_graph(pg) - end - return error("Graph $gname not found") -end - -function loaddot_mult(io::IO) - p = Parsers.DOT.parse_dot(read(io, String)) - graphs = Dict{String,AbstractGraph}() - - for pg in p - graphs[_name(pg)] = _dot_read_one_graph(pg) - end - return graphs -end - -loadgraph(io::IO, gname::String, ::DOTFormat) = loaddot(io, gname) -loadgraphs(io::IO, ::DOTFormat) = loaddot_mult(io) -savegraph(io::IO, g::AbstractGraph, gname::String, ::DOTFormat) = savedot(io, g, gname) -savegraph(io::IO, d::Dict, ::DOTFormat) = savedot_mult(io, d) - -end diff --git a/ext/GraphIOGEXFExt.jl b/ext/GraphIOGEXFExt.jl deleted file mode 100644 index fe2cbf6..0000000 --- a/ext/GraphIOGEXFExt.jl +++ /dev/null @@ -1,58 +0,0 @@ -module GraphIOGEXFExt - -using Graphs -import Graphs: loadgraph, loadgraphs, savegraph, AbstractGraph - -@static if isdefined(Base, :get_extension) - using GraphIO - using EzXML - import GraphIO.GEXF.GEXFFormat -else # not required for julia >= v1.9 - using ..GraphIO - using ..EzXML - import ..GraphIO.GEXF.GEXFFormat -end - -""" - savegexf(f, g, gname) - -Write a graph `g` with name `gname` to an IO stream `io` in the -[Gexf](http://gexf.net/format/) format. Return 1 (number of graphs written). -""" -function savegexf(io::IO, g::AbstractGraph, gname::String) - xdoc = XMLDocument() - xroot = setroot!(xdoc, ElementNode("gexf")) - xroot["xmlns"] = "http://www.gexf.net/1.2draft" - xroot["version"] = "1.2" - xroot["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance" - xroot["xsi:schemaLocation"] = "http://www.gexf.net/1.2draft/gexf.xsd" - - xmeta = addelement!(xroot, "meta") - addelement!(xmeta, "description", gname) - xg = addelement!(xroot, "graph") - strdir = is_directed(g) ? "directed" : "undirected" - xg["defaultedgetype"] = strdir - - xnodes = addelement!(xg, "nodes") - for i in 1:nv(g) - xv = addelement!(xnodes, "node") - xv["id"] = "$(i-1)" - end - - xedges = addelement!(xg, "edges") - m = 0 - for e in edges(g) - xe = addelement!(xedges, "edge") - xe["id"] = "$m" - xe["source"] = "$(src(e)-1)" - xe["target"] = "$(dst(e)-1)" - m += 1 - end - - prettyprint(io, xdoc) - return 1 -end - -savegraph(io::IO, g::AbstractGraph, gname::String, ::GEXFFormat) = savegexf(io, g, gname) - -end diff --git a/ext/GraphIOLGCompressedExt.jl b/ext/GraphIOLGCompressedExt.jl index 96a2c72..4f812d8 100644 --- a/ext/GraphIOLGCompressedExt.jl +++ b/ext/GraphIOLGCompressedExt.jl @@ -3,15 +3,9 @@ module GraphIOLGCompressedExt using Graphs import Graphs: loadgraph, loadgraphs, savegraph, LGFormat -@static if isdefined(Base, :get_extension) - using GraphIO - using CodecZlib - import GraphIO.LGCompressed.LGCompressedFormat -else # not required for julia >= v1.9 - using ..GraphIO - using ..CodecZlib - import ..GraphIO.LGCompressed.LGCompressedFormat -end +using GraphIO +using CodecZlib +import GraphIO.LGCompressed.LGCompressedFormat function savegraph( fn::AbstractString, g::AbstractGraph, gname::AbstractString, format::LGCompressedFormat diff --git a/ext/GraphIOGMLExt.jl b/ext/GraphIOParserCombinator.jl similarity index 51% rename from ext/GraphIOGMLExt.jl rename to ext/GraphIOParserCombinator.jl index 951d498..1bb641d 100644 --- a/ext/GraphIOGMLExt.jl +++ b/ext/GraphIOParserCombinator.jl @@ -1,18 +1,92 @@ -module GraphIOGMLExt +module GraphIOParserCombinator using Graphs import Graphs: loadgraph, loadgraphs, savegraph -@static if isdefined(Base, :get_extension) - using GraphIO - using ParserCombinator - import GraphIO.GML.GMLFormat -else # not required for julia >= v1.9 - using ..GraphIO - using ..ParserCombinator - import ..GraphIO.GML.GMLFormat +using GraphIO +using ParserCombinator +import GraphIO.DOT.DOTFormat +import GraphIO.GML.GMLFormat + +function savedot(io::IO, g::AbstractGraph, gname::String="") + isdir = is_directed(g) + println(io, (isdir ? "digraph " : "graph ") * gname * " {") + for i in vertices(g) + println(io, "\t" * string(i)) + end + if isdir + for u in vertices(g) + out_nbrs = outneighbors(g, u) + length(out_nbrs) == 0 && continue + println(io, "\t" * string(u) * " -> {" * join(out_nbrs, ',') * "}") + end + else + for e in edges(g) + source = string(src(e)) + dest = string(dst(e)) + println(io, "\t" * source * " -- " * dest) + end + end + println(io, "}") + return 1 +end + +function savedot_mult(io::IO, graphs::Dict) + ng = 0 + for (gname, g) in graphs + ng += savedot(io, g, gname) + end + return ng +end + +function _dot_read_one_graph(pg::Parsers.DOT.Graph) + isdir = pg.directed + nvg = length(Parsers.DOT.nodes(pg)) + nodedict = Dict(zip(collect(Parsers.DOT.nodes(pg)), 1:nvg)) + if isdir + g = DiGraph(nvg) + else + g = Graph(nvg) + end + for es in Parsers.DOT.edges(pg) + s = nodedict[es[1]] + d = nodedict[es[2]] + add_edge!(g, s, d) + end + return g +end + +function _name(pg::Parsers.DOT.Graph) + return if pg.id !== nothing + pg.id.id + else + Parsers.DOT.StringID(pg.directed ? "digraph" : "graph") + end +end + +function loaddot(io::IO, gname::String) + p = Parsers.DOT.parse_dot(read(io, String)) + for pg in p + _name(pg) == gname && return _dot_read_one_graph(pg) + end + return error("Graph $gname not found") +end + +function loaddot_mult(io::IO) + p = Parsers.DOT.parse_dot(read(io, String)) + graphs = Dict{String,AbstractGraph}() + + for pg in p + graphs[_name(pg)] = _dot_read_one_graph(pg) + end + return graphs end +loadgraph(io::IO, gname::String, ::DOTFormat) = loaddot(io, gname) +loadgraphs(io::IO, ::DOTFormat) = loaddot_mult(io) +savegraph(io::IO, g::AbstractGraph, gname::String, ::DOTFormat) = savedot(io, g, gname) +savegraph(io::IO, d::Dict, ::DOTFormat) = savedot_mult(io, d) + function _gml_read_one_graph(gs, dir) nodes = [x[:id] for x in gs[:node]] if dir @@ -54,7 +128,7 @@ function loadgml_mult(io::IO) end """ - savegml(f, g, gname="graph") + savegml(f, g, gname="graph") Write a graph `g` with name `gname` to an IO stream `io` in the [GML](https://en.wikipedia.org/wiki/Graph_Modelling_Language) format. Return 1. @@ -83,7 +157,7 @@ function savegml(io::IO, g::AbstractGraph, gname::String="") end """ - savegml_mult(io, graphs) + savegml_mult(io, graphs) Write a dictionary of (name=>graph) to an IO stream `io` Return number of graphs written. """ function savegml_mult(io::IO, graphs::Dict) diff --git a/ext/GraphIOGraphMLExt.jl b/ext/GraphIOXML.jl similarity index 76% rename from ext/GraphIOGraphMLExt.jl rename to ext/GraphIOXML.jl index 99e28b1..4a22fab 100644 --- a/ext/GraphIOGraphMLExt.jl +++ b/ext/GraphIOXML.jl @@ -1,18 +1,55 @@ -module GraphIOGraphMLExt +module GraphIOXML using Graphs -import Graphs: loadgraph, loadgraphs, savegraph - -@static if isdefined(Base, :get_extension) - using GraphIO - using EzXML - import GraphIO.GraphML.GraphMLFormat -else # not required for julia >= v1.9 - using ..GraphIO - using ..EzXML - import ..GraphIO.GraphML.GraphMLFormat +import Graphs: loadgraph, loadgraphs, savegraph, AbstractGraph + +using GraphIO +using EzXML +import GraphIO.GEXF.GEXFFormat +import GraphIO.GraphML.GraphMLFormat + +""" + savegexf(f, g, gname) + +Write a graph `g` with name `gname` to an IO stream `io` in the +[Gexf](http://gexf.net/format/) format. Return 1 (number of graphs written). +""" +function savegexf(io::IO, g::AbstractGraph, gname::String) + xdoc = XMLDocument() + xroot = setroot!(xdoc, ElementNode("gexf")) + xroot["xmlns"] = "http://www.gexf.net/1.2draft" + xroot["version"] = "1.2" + xroot["xmlns:xsi"] = "http://www.w3.org/2001/XMLSchema-instance" + xroot["xsi:schemaLocation"] = "http://www.gexf.net/1.2draft/gexf.xsd" + + xmeta = addelement!(xroot, "meta") + addelement!(xmeta, "description", gname) + xg = addelement!(xroot, "graph") + strdir = is_directed(g) ? "directed" : "undirected" + xg["defaultedgetype"] = strdir + + xnodes = addelement!(xg, "nodes") + for i in 1:nv(g) + xv = addelement!(xnodes, "node") + xv["id"] = "$(i-1)" + end + + xedges = addelement!(xg, "edges") + m = 0 + for e in edges(g) + xe = addelement!(xedges, "edge") + xe["id"] = "$m" + xe["source"] = "$(src(e)-1)" + xe["target"] = "$(dst(e)-1)" + m += 1 + end + + prettyprint(io, xdoc) + return 1 end +savegraph(io::IO, g::AbstractGraph, gname::String, ::GEXFFormat) = savegexf(io, g, gname) + function _graphml_read_one_graph(reader::EzXML.StreamReader, isdirected::Bool) nodes = Dict{String,Int}() xedges = Vector{Edge}() diff --git a/test/runtests.jl b/test/runtests.jl index 20e6b81..8505e85 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -18,7 +18,7 @@ include("graphio.jl") Aqua.test_all(GraphIO) end @testset "Code formatting" begin - @test JuliaFormatter.format(GraphIO; verbose=false, overwrite=false) + @test JuliaFormatter.format(GraphIO; verbose=true, overwrite=false) end for name in modules path = joinpath(testdir, name, "runtests.jl")