mirror of
https://github.com/gonum/gonum.git
synced 2025-10-07 08:01:20 +08:00
graph/encoding/dot: allow unmarshaling of global attributes
This commit is contained in:
@@ -25,6 +25,13 @@ type Builder interface {
|
|||||||
NewEdge(from, to graph.Node) graph.Edge
|
NewEdge(from, to graph.Node) graph.Edge
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmashalerAttrs is implemented by graph values that can unmarshal global
|
||||||
|
// DOT attributes.
|
||||||
|
type UnmarshalerAttrs interface {
|
||||||
|
// DOTUnmarshalerAttrs returns the global attribute unmarshalers.
|
||||||
|
DOTUnmarshalerAttrs() (graph, node, edge UnmarshalerAttr)
|
||||||
|
}
|
||||||
|
|
||||||
// UnmarshalerAttr is implemented by types that can unmarshal a DOT
|
// UnmarshalerAttr is implemented by types that can unmarshal a DOT
|
||||||
// attribute description of themselves.
|
// attribute description of themselves.
|
||||||
type UnmarshalerAttr interface {
|
type UnmarshalerAttr interface {
|
||||||
@@ -60,6 +67,9 @@ func copyGraph(dst Builder, src *ast.Graph) (err error) {
|
|||||||
directed: src.Directed,
|
directed: src.Directed,
|
||||||
ids: make(map[string]graph.Node),
|
ids: make(map[string]graph.Node),
|
||||||
}
|
}
|
||||||
|
if a, ok := dst.(UnmarshalerAttrs); ok {
|
||||||
|
gen.graphAttr, gen.nodeAttr, gen.edgeAttr = a.DOTUnmarshalerAttrs()
|
||||||
|
}
|
||||||
for _, stmt := range src.Stmts {
|
for _, stmt := range src.Stmts {
|
||||||
gen.addStmt(dst, stmt)
|
gen.addStmt(dst, stmt)
|
||||||
}
|
}
|
||||||
@@ -79,6 +89,8 @@ type generator struct {
|
|||||||
// Stack of start indices into the subgraph node slice. The top element
|
// Stack of start indices into the subgraph node slice. The top element
|
||||||
// corresponds to the start index of the active (or inner-most) subgraph.
|
// corresponds to the start index of the active (or inner-most) subgraph.
|
||||||
subStart []int
|
subStart []int
|
||||||
|
// graphAttr, nodeAttr and edgeAttr are global graph attributes.
|
||||||
|
graphAttr, nodeAttr, edgeAttr UnmarshalerAttr
|
||||||
}
|
}
|
||||||
|
|
||||||
// node returns the gonum node corresponding to the given dot AST node ID,
|
// node returns the gonum node corresponding to the given dot AST node ID,
|
||||||
@@ -119,7 +131,39 @@ func (gen *generator) addStmt(dst Builder, stmt ast.Stmt) {
|
|||||||
case *ast.EdgeStmt:
|
case *ast.EdgeStmt:
|
||||||
gen.addEdgeStmt(dst, stmt)
|
gen.addEdgeStmt(dst, stmt)
|
||||||
case *ast.AttrStmt:
|
case *ast.AttrStmt:
|
||||||
// ignore.
|
var n UnmarshalerAttr
|
||||||
|
var dst string
|
||||||
|
switch stmt.Kind {
|
||||||
|
case ast.KindGraph:
|
||||||
|
if gen.graphAttr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n = gen.graphAttr
|
||||||
|
dst = "graph"
|
||||||
|
case ast.KindNode:
|
||||||
|
if gen.nodeAttr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n = gen.nodeAttr
|
||||||
|
dst = "node"
|
||||||
|
case ast.KindEdge:
|
||||||
|
if gen.edgeAttr == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n = gen.edgeAttr
|
||||||
|
dst = "edge"
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
for _, attr := range stmt.Attrs {
|
||||||
|
a := Attribute{
|
||||||
|
Key: attr.Key,
|
||||||
|
Value: attr.Val,
|
||||||
|
}
|
||||||
|
if err := n.UnmarshalDOTAttr(a); err != nil {
|
||||||
|
panic(fmt.Errorf("unable to unmarshal global %s DOT attribute (%s=%s)", dst, a.Key, a.Value))
|
||||||
|
}
|
||||||
|
}
|
||||||
case *ast.Attr:
|
case *ast.Attr:
|
||||||
// ignore.
|
// ignore.
|
||||||
case *ast.Subgraph:
|
case *ast.Subgraph:
|
||||||
|
@@ -45,13 +45,25 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
}
|
}
|
||||||
got := string(buf)
|
got := string(buf)
|
||||||
if got != g.want {
|
if got != g.want {
|
||||||
t.Errorf("i=%d: graph content mismatch; expected `%s`, got `%s`", i, g.want, got)
|
t.Errorf("i=%d: graph content mismatch; want:\n%s\n\ngot:\n%s", i, g.want, got)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const directed = `digraph {
|
const directed = `digraph {
|
||||||
|
graph [
|
||||||
|
outputorder=edgesfirst
|
||||||
|
];
|
||||||
|
node [
|
||||||
|
shape=circle
|
||||||
|
style=filled
|
||||||
|
];
|
||||||
|
edge [
|
||||||
|
penwidth=5
|
||||||
|
color=gray
|
||||||
|
];
|
||||||
|
|
||||||
// Node definitions.
|
// Node definitions.
|
||||||
0 [label="foo 2"];
|
0 [label="foo 2"];
|
||||||
1 [label="bar 2"];
|
1 [label="bar 2"];
|
||||||
@@ -61,6 +73,18 @@ const directed = `digraph {
|
|||||||
}`
|
}`
|
||||||
|
|
||||||
const undirected = `graph {
|
const undirected = `graph {
|
||||||
|
graph [
|
||||||
|
outputorder=edgesfirst
|
||||||
|
];
|
||||||
|
node [
|
||||||
|
shape=circle
|
||||||
|
style=filled
|
||||||
|
];
|
||||||
|
edge [
|
||||||
|
penwidth=5
|
||||||
|
color=gray
|
||||||
|
];
|
||||||
|
|
||||||
// Node definitions.
|
// Node definitions.
|
||||||
0 [label="foo 2"];
|
0 [label="foo 2"];
|
||||||
1 [label="bar 2"];
|
1 [label="bar 2"];
|
||||||
@@ -79,6 +103,7 @@ const undirected = `graph {
|
|||||||
// dotDirectedGraph implements the dot.Builder interface.
|
// dotDirectedGraph implements the dot.Builder interface.
|
||||||
type dotDirectedGraph struct {
|
type dotDirectedGraph struct {
|
||||||
*simple.DirectedGraph
|
*simple.DirectedGraph
|
||||||
|
graph, node, edge attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
// newDotDirectedGraph returns a new directed capable of creating user-defined
|
// newDotDirectedGraph returns a new directed capable of creating user-defined
|
||||||
@@ -105,12 +130,23 @@ func (g *dotDirectedGraph) NewEdge(from, to graph.Node) graph.Edge {
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DOTAttributers implements the dot.Attributers interface.
|
||||||
|
func (g *dotDirectedGraph) DOTAttributers() (graph, node, edge Attributer) {
|
||||||
|
return g.graph, g.node, g.edge
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface.
|
||||||
|
func (g *dotDirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge UnmarshalerAttr) {
|
||||||
|
return &g.graph, &g.node, &g.edge
|
||||||
|
}
|
||||||
|
|
||||||
// dotUndirectedGraph extends simple.UndirectedGraph to add NewNode and NewEdge
|
// dotUndirectedGraph extends simple.UndirectedGraph to add NewNode and NewEdge
|
||||||
// methods for creating user-defined nodes and edges.
|
// methods for creating user-defined nodes and edges.
|
||||||
//
|
//
|
||||||
// dotUndirectedGraph implements the dot.Builder interface.
|
// dotUndirectedGraph implements the dot.Builder interface.
|
||||||
type dotUndirectedGraph struct {
|
type dotUndirectedGraph struct {
|
||||||
*simple.UndirectedGraph
|
*simple.UndirectedGraph
|
||||||
|
graph, node, edge attributes
|
||||||
}
|
}
|
||||||
|
|
||||||
// newDotUndirectedGraph returns a new undirected capable of creating user-
|
// newDotUndirectedGraph returns a new undirected capable of creating user-
|
||||||
@@ -137,6 +173,16 @@ func (g *dotUndirectedGraph) NewEdge(from, to graph.Node) graph.Edge {
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DOTAttributers implements the dot.Attributers interface.
|
||||||
|
func (g *dotUndirectedGraph) DOTAttributers() (graph, node, edge Attributer) {
|
||||||
|
return g.graph, g.node, g.edge
|
||||||
|
}
|
||||||
|
|
||||||
|
// DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface.
|
||||||
|
func (g *dotUndirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge UnmarshalerAttr) {
|
||||||
|
return &g.graph, &g.node, &g.edge
|
||||||
|
}
|
||||||
|
|
||||||
// dotNode extends simple.Node with a label field to test round-trip encoding
|
// dotNode extends simple.Node with a label field to test round-trip encoding
|
||||||
// and decoding of node DOT label attributes.
|
// and decoding of node DOT label attributes.
|
||||||
type dotNode struct {
|
type dotNode struct {
|
||||||
@@ -194,3 +240,14 @@ func (e *dotEdge) DOTAttributes() []Attribute {
|
|||||||
}
|
}
|
||||||
return []Attribute{attr}
|
return []Attribute{attr}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// attributes is a helper for global attributes.
|
||||||
|
type attributes []Attribute
|
||||||
|
|
||||||
|
func (a attributes) DOTAttributes() []Attribute {
|
||||||
|
return []Attribute(a)
|
||||||
|
}
|
||||||
|
func (a *attributes) UnmarshalDOTAttr(attr Attribute) error {
|
||||||
|
*a = append(*a, attr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user