mirror of
https://github.com/gonum/gonum.git
synced 2025-10-05 23:26:52 +08:00
graph/encoding: move basic encoding API into new encoding package
This commit is contained in:
@@ -8,32 +8,17 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
|
"gonum.org/v1/gonum/graph/encoding"
|
||||||
"gonum.org/v1/gonum/graph/formats/dot"
|
"gonum.org/v1/gonum/graph/formats/dot"
|
||||||
"gonum.org/v1/gonum/graph/formats/dot/ast"
|
"gonum.org/v1/gonum/graph/formats/dot/ast"
|
||||||
"gonum.org/v1/gonum/graph/internal/set"
|
"gonum.org/v1/gonum/graph/internal/set"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Builder is a graph that can have user-defined nodes and edges added.
|
|
||||||
type Builder interface {
|
|
||||||
graph.Graph
|
|
||||||
graph.Builder
|
|
||||||
// NewEdge adds a new edge from the source to the destination node to the
|
|
||||||
// graph, or returns the existing edge if already present.
|
|
||||||
NewEdge(from, to graph.Node) graph.Edge
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmashalerAttrs is implemented by graph values that can unmarshal global
|
// UnmashalerAttrs is implemented by graph values that can unmarshal global
|
||||||
// DOT attributes.
|
// DOT attributes.
|
||||||
type UnmarshalerAttrs interface {
|
type UnmarshalerAttrs interface {
|
||||||
// DOTUnmarshalerAttrs returns the global attribute unmarshalers.
|
// DOTUnmarshalerAttrs returns the global attribute unmarshalers.
|
||||||
DOTUnmarshalerAttrs() (graph, node, edge UnmarshalerAttr)
|
DOTUnmarshalerAttrs() (graph, node, edge encoding.UnmarshalerAttr)
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalerAttr is implemented by types that can unmarshal a DOT
|
|
||||||
// attribute description of themselves.
|
|
||||||
type UnmarshalerAttr interface {
|
|
||||||
// UnmarshalDOTAttr decodes a single DOT attribute.
|
|
||||||
UnmarshalDOTAttr(attr Attribute) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalerID is implemented by types that can unmarshal a DOT ID.
|
// UnmarshalerID is implemented by types that can unmarshal a DOT ID.
|
||||||
@@ -43,7 +28,7 @@ type UnmarshalerID interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal parses the Graphviz DOT-encoded data and stores the result in dst.
|
// Unmarshal parses the Graphviz DOT-encoded data and stores the result in dst.
|
||||||
func Unmarshal(data []byte, dst Builder) error {
|
func Unmarshal(data []byte, dst encoding.Builder) error {
|
||||||
file, err := dot.ParseBytes(data)
|
file, err := dot.ParseBytes(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -56,7 +41,7 @@ func Unmarshal(data []byte, dst Builder) error {
|
|||||||
|
|
||||||
// copyGraph copies the nodes and edges from the Graphviz AST source graph to
|
// copyGraph copies the nodes and edges from the Graphviz AST source graph to
|
||||||
// the destination graph. Edge direction is maintained if present.
|
// the destination graph. Edge direction is maintained if present.
|
||||||
func copyGraph(dst Builder, src *ast.Graph) (err error) {
|
func copyGraph(dst encoding.Builder, src *ast.Graph) (err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
switch e := recover().(type) {
|
switch e := recover().(type) {
|
||||||
case nil:
|
case nil:
|
||||||
@@ -93,12 +78,12 @@ type generator struct {
|
|||||||
// 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 and edgeAttr are global graph attributes.
|
||||||
graphAttr, nodeAttr, edgeAttr UnmarshalerAttr
|
graphAttr, nodeAttr, edgeAttr encoding.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,
|
||||||
// generating a new such node if none exist.
|
// generating a new such node if none exist.
|
||||||
func (gen *generator) node(dst Builder, id string) graph.Node {
|
func (gen *generator) node(dst encoding.Builder, id string) graph.Node {
|
||||||
if n, ok := gen.ids[id]; ok {
|
if n, ok := gen.ids[id]; ok {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
@@ -119,26 +104,26 @@ func (gen *generator) node(dst Builder, id string) graph.Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addStmt adds the given statement to the graph.
|
// addStmt adds the given statement to the graph.
|
||||||
func (gen *generator) addStmt(dst Builder, stmt ast.Stmt) {
|
func (gen *generator) addStmt(dst encoding.Builder, stmt ast.Stmt) {
|
||||||
switch stmt := stmt.(type) {
|
switch stmt := stmt.(type) {
|
||||||
case *ast.NodeStmt:
|
case *ast.NodeStmt:
|
||||||
n, ok := gen.node(dst, stmt.Node.ID).(UnmarshalerAttr)
|
n, ok := gen.node(dst, stmt.Node.ID).(encoding.UnmarshalerAttr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, attr := range stmt.Attrs {
|
for _, attr := range stmt.Attrs {
|
||||||
a := Attribute{
|
a := encoding.Attribute{
|
||||||
Key: attr.Key,
|
Key: attr.Key,
|
||||||
Value: attr.Val,
|
Value: attr.Val,
|
||||||
}
|
}
|
||||||
if err := n.UnmarshalDOTAttr(a); err != nil {
|
if err := n.UnmarshalAttr(a); err != nil {
|
||||||
panic(fmt.Errorf("unable to unmarshal node DOT attribute (%s=%s)", a.Key, a.Value))
|
panic(fmt.Errorf("unable to unmarshal node DOT attribute (%s=%s)", a.Key, a.Value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *ast.EdgeStmt:
|
case *ast.EdgeStmt:
|
||||||
gen.addEdgeStmt(dst, stmt)
|
gen.addEdgeStmt(dst, stmt)
|
||||||
case *ast.AttrStmt:
|
case *ast.AttrStmt:
|
||||||
var n UnmarshalerAttr
|
var n encoding.UnmarshalerAttr
|
||||||
var dst string
|
var dst string
|
||||||
switch stmt.Kind {
|
switch stmt.Kind {
|
||||||
case ast.GraphKind:
|
case ast.GraphKind:
|
||||||
@@ -163,11 +148,11 @@ func (gen *generator) addStmt(dst Builder, stmt ast.Stmt) {
|
|||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
for _, attr := range stmt.Attrs {
|
for _, attr := range stmt.Attrs {
|
||||||
a := Attribute{
|
a := encoding.Attribute{
|
||||||
Key: attr.Key,
|
Key: attr.Key,
|
||||||
Value: attr.Val,
|
Value: attr.Val,
|
||||||
}
|
}
|
||||||
if err := n.UnmarshalDOTAttr(a); err != nil {
|
if err := n.UnmarshalAttr(a); err != nil {
|
||||||
panic(fmt.Errorf("unable to unmarshal global %s DOT attribute (%s=%s)", dst, a.Key, a.Value))
|
panic(fmt.Errorf("unable to unmarshal global %s DOT attribute (%s=%s)", dst, a.Key, a.Value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,21 +168,21 @@ func (gen *generator) addStmt(dst Builder, stmt ast.Stmt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addEdgeStmt adds the given edge statement to the graph.
|
// addEdgeStmt adds the given edge statement to the graph.
|
||||||
func (gen *generator) addEdgeStmt(dst Builder, e *ast.EdgeStmt) {
|
func (gen *generator) addEdgeStmt(dst encoding.Builder, e *ast.EdgeStmt) {
|
||||||
fs := gen.addVertex(dst, e.From)
|
fs := gen.addVertex(dst, e.From)
|
||||||
ts := gen.addEdge(dst, e.To)
|
ts := gen.addEdge(dst, e.To)
|
||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
for _, t := range ts {
|
for _, t := range ts {
|
||||||
edge, ok := dst.NewEdge(f, t).(UnmarshalerAttr)
|
edge, ok := dst.NewEdge(f, t).(encoding.UnmarshalerAttr)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, attr := range e.Attrs {
|
for _, attr := range e.Attrs {
|
||||||
a := Attribute{
|
a := encoding.Attribute{
|
||||||
Key: attr.Key,
|
Key: attr.Key,
|
||||||
Value: attr.Val,
|
Value: attr.Val,
|
||||||
}
|
}
|
||||||
if err := edge.UnmarshalDOTAttr(a); err != nil {
|
if err := edge.UnmarshalAttr(a); err != nil {
|
||||||
panic(fmt.Errorf("unable to unmarshal edge DOT attribute (%s=%s)", a.Key, a.Value))
|
panic(fmt.Errorf("unable to unmarshal edge DOT attribute (%s=%s)", a.Key, a.Value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,7 +191,7 @@ func (gen *generator) addEdgeStmt(dst Builder, e *ast.EdgeStmt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addVertex adds the given vertex to the graph, and returns its set of nodes.
|
// addVertex adds the given vertex to the graph, and returns its set of nodes.
|
||||||
func (gen *generator) addVertex(dst Builder, v ast.Vertex) []graph.Node {
|
func (gen *generator) addVertex(dst encoding.Builder, v ast.Vertex) []graph.Node {
|
||||||
switch v := v.(type) {
|
switch v := v.(type) {
|
||||||
case *ast.Node:
|
case *ast.Node:
|
||||||
n := gen.node(dst, v.ID)
|
n := gen.node(dst, v.ID)
|
||||||
@@ -223,7 +208,7 @@ func (gen *generator) addVertex(dst Builder, v ast.Vertex) []graph.Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// addEdge adds the given edge to the graph, and returns its set of nodes.
|
// addEdge adds the given edge to the graph, and returns its set of nodes.
|
||||||
func (gen *generator) addEdge(dst Builder, to *ast.Edge) []graph.Node {
|
func (gen *generator) addEdge(dst encoding.Builder, to *ast.Edge) []graph.Node {
|
||||||
if !gen.directed && to.Directed {
|
if !gen.directed && to.Directed {
|
||||||
panic(fmt.Errorf("directed edge to %v in undirected graph", to.Vertex))
|
panic(fmt.Errorf("directed edge to %v in undirected graph", to.Vertex))
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
|
"gonum.org/v1/gonum/graph/encoding"
|
||||||
"gonum.org/v1/gonum/graph/simple"
|
"gonum.org/v1/gonum/graph/simple"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ func TestRoundTrip(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for i, g := range golden {
|
for i, g := range golden {
|
||||||
var dst Builder
|
var dst encoding.Builder
|
||||||
if g.directed {
|
if g.directed {
|
||||||
dst = newDotDirectedGraph()
|
dst = newDotDirectedGraph()
|
||||||
} else {
|
} else {
|
||||||
@@ -129,12 +130,12 @@ func (g *dotDirectedGraph) NewEdge(from, to graph.Node) graph.Edge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DOTAttributers implements the dot.Attributers interface.
|
// DOTAttributers implements the dot.Attributers interface.
|
||||||
func (g *dotDirectedGraph) DOTAttributers() (graph, node, edge Attributer) {
|
func (g *dotDirectedGraph) DOTAttributers() (graph, node, edge encoding.Attributer) {
|
||||||
return g.graph, g.node, g.edge
|
return g.graph, g.node, g.edge
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface.
|
// DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface.
|
||||||
func (g *dotDirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge UnmarshalerAttr) {
|
func (g *dotDirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge encoding.UnmarshalerAttr) {
|
||||||
return &g.graph, &g.node, &g.edge
|
return &g.graph, &g.node, &g.edge
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,12 +171,12 @@ func (g *dotUndirectedGraph) NewEdge(from, to graph.Node) graph.Edge {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DOTAttributers implements the dot.Attributers interface.
|
// DOTAttributers implements the dot.Attributers interface.
|
||||||
func (g *dotUndirectedGraph) DOTAttributers() (graph, node, edge Attributer) {
|
func (g *dotUndirectedGraph) DOTAttributers() (graph, node, edge encoding.Attributer) {
|
||||||
return g.graph, g.node, g.edge
|
return g.graph, g.node, g.edge
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface.
|
// DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface.
|
||||||
func (g *dotUndirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge UnmarshalerAttr) {
|
func (g *dotUndirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge encoding.UnmarshalerAttr) {
|
||||||
return &g.graph, &g.node, &g.edge
|
return &g.graph, &g.node, &g.edge
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,8 +199,8 @@ func (n *dotNode) UnmarshalDOTID(id string) {
|
|||||||
n.dotID = id
|
n.dotID = id
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalDOTAttr decodes a single DOT attribute.
|
// UnmarshalAttr decodes a single DOT attribute.
|
||||||
func (n *dotNode) UnmarshalDOTAttr(attr Attribute) error {
|
func (n *dotNode) UnmarshalAttr(attr encoding.Attribute) error {
|
||||||
if attr.Key != "label" {
|
if attr.Key != "label" {
|
||||||
return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key)
|
return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key)
|
||||||
}
|
}
|
||||||
@@ -207,16 +208,16 @@ func (n *dotNode) UnmarshalDOTAttr(attr Attribute) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOTAttributes returns the DOT attributes of the node.
|
// Attributes returns the DOT attributes of the node.
|
||||||
func (n *dotNode) DOTAttributes() []Attribute {
|
func (n *dotNode) Attributes() []encoding.Attribute {
|
||||||
if len(n.Label) == 0 {
|
if len(n.Label) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
attr := Attribute{
|
attr := encoding.Attribute{
|
||||||
Key: "label",
|
Key: "label",
|
||||||
Value: n.Label,
|
Value: n.Label,
|
||||||
}
|
}
|
||||||
return []Attribute{attr}
|
return []encoding.Attribute{attr}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dotEdge extends simple.Edge with a label field to test round-trip encoding and
|
// dotEdge extends simple.Edge with a label field to test round-trip encoding and
|
||||||
@@ -227,8 +228,8 @@ type dotEdge struct {
|
|||||||
Label string
|
Label string
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalDOTAttr decodes a single DOT attribute.
|
// UnmarshalAttr decodes a single DOT attribute.
|
||||||
func (e *dotEdge) UnmarshalDOTAttr(attr Attribute) error {
|
func (e *dotEdge) UnmarshalAttr(attr encoding.Attribute) error {
|
||||||
if attr.Key != "label" {
|
if attr.Key != "label" {
|
||||||
return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key)
|
return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key)
|
||||||
}
|
}
|
||||||
@@ -236,25 +237,25 @@ func (e *dotEdge) UnmarshalDOTAttr(attr Attribute) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOTAttributes returns the DOT attributes of the edge.
|
// Attributes returns the DOT attributes of the edge.
|
||||||
func (e *dotEdge) DOTAttributes() []Attribute {
|
func (e *dotEdge) Attributes() []encoding.Attribute {
|
||||||
if len(e.Label) == 0 {
|
if len(e.Label) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
attr := Attribute{
|
attr := encoding.Attribute{
|
||||||
Key: "label",
|
Key: "label",
|
||||||
Value: e.Label,
|
Value: e.Label,
|
||||||
}
|
}
|
||||||
return []Attribute{attr}
|
return []encoding.Attribute{attr}
|
||||||
}
|
}
|
||||||
|
|
||||||
// attributes is a helper for global attributes.
|
// attributes is a helper for global attributes.
|
||||||
type attributes []Attribute
|
type attributes []encoding.Attribute
|
||||||
|
|
||||||
func (a attributes) DOTAttributes() []Attribute {
|
func (a attributes) Attributes() []encoding.Attribute {
|
||||||
return []Attribute(a)
|
return []encoding.Attribute(a)
|
||||||
}
|
}
|
||||||
func (a *attributes) UnmarshalDOTAttr(attr Attribute) error {
|
func (a *attributes) UnmarshalAttr(attr encoding.Attribute) error {
|
||||||
*a = append(*a, attr)
|
*a = append(*a, attr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
|
"gonum.org/v1/gonum/graph/encoding"
|
||||||
"gonum.org/v1/gonum/graph/internal/ordered"
|
"gonum.org/v1/gonum/graph/internal/ordered"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,18 +33,7 @@ type Node interface {
|
|||||||
// Attributers are graph.Graph values that specify top-level DOT
|
// Attributers are graph.Graph values that specify top-level DOT
|
||||||
// attributes.
|
// attributes.
|
||||||
type Attributers interface {
|
type Attributers interface {
|
||||||
DOTAttributers() (graph, node, edge Attributer)
|
DOTAttributers() (graph, node, edge encoding.Attributer)
|
||||||
}
|
|
||||||
|
|
||||||
// Attributer defines graph.Node or graph.Edge values that can
|
|
||||||
// specify DOT attributes.
|
|
||||||
type Attributer interface {
|
|
||||||
DOTAttributes() []Attribute
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attribute is a DOT language key value attribute pair.
|
|
||||||
type Attribute struct {
|
|
||||||
Key, Value string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Porter defines the behavior of graph.Edge values that can specify
|
// Porter defines the behavior of graph.Edge values that can specify
|
||||||
@@ -184,7 +174,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool
|
|||||||
}
|
}
|
||||||
p.newline()
|
p.newline()
|
||||||
p.writeNode(n)
|
p.writeNode(n)
|
||||||
if a, ok := n.(Attributer); ok {
|
if a, ok := n.(encoding.Attributer); ok {
|
||||||
p.writeAttributeList(a)
|
p.writeAttributeList(a)
|
||||||
}
|
}
|
||||||
p.buf.WriteByte(';')
|
p.buf.WriteByte(';')
|
||||||
@@ -252,7 +242,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool
|
|||||||
p.writePorts(e.ToPort())
|
p.writePorts(e.ToPort())
|
||||||
}
|
}
|
||||||
|
|
||||||
if a, ok := g.Edge(n, t).(Attributer); ok {
|
if a, ok := g.Edge(n, t).(encoding.Attributer); ok {
|
||||||
p.writeAttributeList(a)
|
p.writeAttributeList(a)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,8 +287,8 @@ func graphID(g graph.Graph, n graph.Node) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) writeAttributeList(a Attributer) {
|
func (p *printer) writeAttributeList(a encoding.Attributer) {
|
||||||
attributes := a.DOTAttributes()
|
attributes := a.Attributes()
|
||||||
switch len(attributes) {
|
switch len(attributes) {
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
@@ -324,8 +314,8 @@ var attType = []string{"graph", "node", "edge"}
|
|||||||
func (p *printer) writeAttributeComplex(ca Attributers) {
|
func (p *printer) writeAttributeComplex(ca Attributers) {
|
||||||
g, n, e := ca.DOTAttributers()
|
g, n, e := ca.DOTAttributers()
|
||||||
haveWrittenBlock := false
|
haveWrittenBlock := false
|
||||||
for i, a := range []Attributer{g, n, e} {
|
for i, a := range []encoding.Attributer{g, n, e} {
|
||||||
attributes := a.DOTAttributes()
|
attributes := a.Attributes()
|
||||||
if len(attributes) == 0 {
|
if len(attributes) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"gonum.org/v1/gonum/graph"
|
"gonum.org/v1/gonum/graph"
|
||||||
|
"gonum.org/v1/gonum/graph/encoding"
|
||||||
"gonum.org/v1/gonum/graph/simple"
|
"gonum.org/v1/gonum/graph/simple"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -112,17 +113,17 @@ func undirectedNamedIDGraphFrom(g []intset) graph.Graph {
|
|||||||
type attrNode struct {
|
type attrNode struct {
|
||||||
id int64
|
id int64
|
||||||
name string
|
name string
|
||||||
attr []Attribute
|
attr []encoding.Attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n attrNode) ID() int64 { return n.id }
|
func (n attrNode) ID() int64 { return n.id }
|
||||||
func (n attrNode) DOTAttributes() []Attribute { return n.attr }
|
func (n attrNode) Attributes() []encoding.Attribute { return n.attr }
|
||||||
|
|
||||||
func directedNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Directed {
|
func directedNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Directed {
|
||||||
dg := simple.NewDirectedGraph(0, math.Inf(1))
|
dg := simple.NewDirectedGraph(0, math.Inf(1))
|
||||||
for u, e := range g {
|
for u, e := range g {
|
||||||
u := int64(u)
|
u := int64(u)
|
||||||
var at []Attribute
|
var at []encoding.Attribute
|
||||||
if u < int64(len(attr)) {
|
if u < int64(len(attr)) {
|
||||||
at = attr[u]
|
at = attr[u]
|
||||||
}
|
}
|
||||||
@@ -138,11 +139,11 @@ func directedNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Directed {
|
|||||||
return dg
|
return dg
|
||||||
}
|
}
|
||||||
|
|
||||||
func undirectedNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Graph {
|
func undirectedNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Graph {
|
||||||
dg := simple.NewUndirectedGraph(0, math.Inf(1))
|
dg := simple.NewUndirectedGraph(0, math.Inf(1))
|
||||||
for u, e := range g {
|
for u, e := range g {
|
||||||
u := int64(u)
|
u := int64(u)
|
||||||
var at []Attribute
|
var at []encoding.Attribute
|
||||||
if u < int64(len(attr)) {
|
if u < int64(len(attr)) {
|
||||||
at = attr[u]
|
at = attr[u]
|
||||||
}
|
}
|
||||||
@@ -161,18 +162,18 @@ func undirectedNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Graph {
|
|||||||
type namedAttrNode struct {
|
type namedAttrNode struct {
|
||||||
id int64
|
id int64
|
||||||
name string
|
name string
|
||||||
attr []Attribute
|
attr []encoding.Attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n namedAttrNode) ID() int64 { return n.id }
|
func (n namedAttrNode) ID() int64 { return n.id }
|
||||||
func (n namedAttrNode) DOTID() string { return n.name }
|
func (n namedAttrNode) DOTID() string { return n.name }
|
||||||
func (n namedAttrNode) DOTAttributes() []Attribute { return n.attr }
|
func (n namedAttrNode) Attributes() []encoding.Attribute { return n.attr }
|
||||||
|
|
||||||
func directedNamedIDNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Directed {
|
func directedNamedIDNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Directed {
|
||||||
dg := simple.NewDirectedGraph(0, math.Inf(1))
|
dg := simple.NewDirectedGraph(0, math.Inf(1))
|
||||||
for u, e := range g {
|
for u, e := range g {
|
||||||
u := int64(u)
|
u := int64(u)
|
||||||
var at []Attribute
|
var at []encoding.Attribute
|
||||||
if u < int64(len(attr)) {
|
if u < int64(len(attr)) {
|
||||||
at = attr[u]
|
at = attr[u]
|
||||||
}
|
}
|
||||||
@@ -188,11 +189,11 @@ func directedNamedIDNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Dire
|
|||||||
return dg
|
return dg
|
||||||
}
|
}
|
||||||
|
|
||||||
func undirectedNamedIDNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Graph {
|
func undirectedNamedIDNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Graph {
|
||||||
dg := simple.NewUndirectedGraph(0, math.Inf(1))
|
dg := simple.NewUndirectedGraph(0, math.Inf(1))
|
||||||
for u, e := range g {
|
for u, e := range g {
|
||||||
u := int64(u)
|
u := int64(u)
|
||||||
var at []Attribute
|
var at []encoding.Attribute
|
||||||
if u < int64(len(attr)) {
|
if u < int64(len(attr)) {
|
||||||
at = attr[u]
|
at = attr[u]
|
||||||
}
|
}
|
||||||
@@ -211,15 +212,15 @@ func undirectedNamedIDNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Gr
|
|||||||
type attrEdge struct {
|
type attrEdge struct {
|
||||||
from, to graph.Node
|
from, to graph.Node
|
||||||
|
|
||||||
attr []Attribute
|
attr []encoding.Attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e attrEdge) From() graph.Node { return e.from }
|
func (e attrEdge) From() graph.Node { return e.from }
|
||||||
func (e attrEdge) To() graph.Node { return e.to }
|
func (e attrEdge) To() graph.Node { return e.to }
|
||||||
func (e attrEdge) Weight() float64 { return 0 }
|
func (e attrEdge) Weight() float64 { return 0 }
|
||||||
func (e attrEdge) DOTAttributes() []Attribute { return e.attr }
|
func (e attrEdge) Attributes() []encoding.Attribute { return e.attr }
|
||||||
|
|
||||||
func directedEdgeAttrGraphFrom(g []intset, attr map[edge][]Attribute) graph.Directed {
|
func directedEdgeAttrGraphFrom(g []intset, attr map[edge][]encoding.Attribute) graph.Directed {
|
||||||
dg := simple.NewDirectedGraph(0, math.Inf(1))
|
dg := simple.NewDirectedGraph(0, math.Inf(1))
|
||||||
for u, e := range g {
|
for u, e := range g {
|
||||||
u := int64(u)
|
u := int64(u)
|
||||||
@@ -230,7 +231,7 @@ func directedEdgeAttrGraphFrom(g []intset, attr map[edge][]Attribute) graph.Dire
|
|||||||
return dg
|
return dg
|
||||||
}
|
}
|
||||||
|
|
||||||
func undirectedEdgeAttrGraphFrom(g []intset, attr map[edge][]Attribute) graph.Graph {
|
func undirectedEdgeAttrGraphFrom(g []intset, attr map[edge][]encoding.Attribute) graph.Graph {
|
||||||
dg := simple.NewUndirectedGraph(0, math.Inf(1))
|
dg := simple.NewUndirectedGraph(0, math.Inf(1))
|
||||||
for u, e := range g {
|
for u, e := range g {
|
||||||
u := int64(u)
|
u := int64(u)
|
||||||
@@ -271,11 +272,11 @@ func (e portedEdge) ToPort() (port, compass string) {
|
|||||||
return e.toPort, e.toCompass
|
return e.toPort, e.toCompass
|
||||||
}
|
}
|
||||||
|
|
||||||
func directedPortedAttrGraphFrom(g []intset, attr [][]Attribute, ports map[edge]portedEdge) graph.Directed {
|
func directedPortedAttrGraphFrom(g []intset, attr [][]encoding.Attribute, ports map[edge]portedEdge) graph.Directed {
|
||||||
dg := simple.NewDirectedGraph(0, math.Inf(1))
|
dg := simple.NewDirectedGraph(0, math.Inf(1))
|
||||||
for u, e := range g {
|
for u, e := range g {
|
||||||
u := int64(u)
|
u := int64(u)
|
||||||
var at []Attribute
|
var at []encoding.Attribute
|
||||||
if u < int64(len(attr)) {
|
if u < int64(len(attr)) {
|
||||||
at = attr[u]
|
at = attr[u]
|
||||||
}
|
}
|
||||||
@@ -293,11 +294,11 @@ func directedPortedAttrGraphFrom(g []intset, attr [][]Attribute, ports map[edge]
|
|||||||
return dg
|
return dg
|
||||||
}
|
}
|
||||||
|
|
||||||
func undirectedPortedAttrGraphFrom(g []intset, attr [][]Attribute, ports map[edge]portedEdge) graph.Graph {
|
func undirectedPortedAttrGraphFrom(g []intset, attr [][]encoding.Attribute, ports map[edge]portedEdge) graph.Graph {
|
||||||
dg := simple.NewUndirectedGraph(0, math.Inf(1))
|
dg := simple.NewUndirectedGraph(0, math.Inf(1))
|
||||||
for u, e := range g {
|
for u, e := range g {
|
||||||
u := int64(u)
|
u := int64(u)
|
||||||
var at []Attribute
|
var at []encoding.Attribute
|
||||||
if u < int64(len(attr)) {
|
if u < int64(len(attr)) {
|
||||||
at = attr[u]
|
at = attr[u]
|
||||||
}
|
}
|
||||||
@@ -322,11 +323,11 @@ type graphAttributer struct {
|
|||||||
edge attributer
|
edge attributer
|
||||||
}
|
}
|
||||||
|
|
||||||
type attributer []Attribute
|
type attributer []encoding.Attribute
|
||||||
|
|
||||||
func (a attributer) DOTAttributes() []Attribute { return a }
|
func (a attributer) Attributes() []encoding.Attribute { return a }
|
||||||
|
|
||||||
func (g graphAttributer) DOTAttributers() (graph, node, edge Attributer) {
|
func (g graphAttributer) DOTAttributers() (graph, node, edge encoding.Attributer) {
|
||||||
return g.graph, g.node, g.edge
|
return g.graph, g.node, g.edge
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -748,7 +749,7 @@ var encodeTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
g: directedNodeAttrGraphFrom(powerMethodGraph, [][]Attribute{
|
g: directedNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{
|
||||||
2: {{"fontsize", "16"}, {"shape", "ellipse"}},
|
2: {{"fontsize", "16"}, {"shape", "ellipse"}},
|
||||||
4: {},
|
4: {},
|
||||||
}),
|
}),
|
||||||
@@ -775,7 +776,7 @@ var encodeTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
g: undirectedNodeAttrGraphFrom(powerMethodGraph, [][]Attribute{
|
g: undirectedNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{
|
||||||
2: {{"fontsize", "16"}, {"shape", "ellipse"}},
|
2: {{"fontsize", "16"}, {"shape", "ellipse"}},
|
||||||
4: {},
|
4: {},
|
||||||
}),
|
}),
|
||||||
@@ -802,7 +803,7 @@ var encodeTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
g: directedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]Attribute{
|
g: directedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{
|
||||||
2: {{"fontsize", "16"}, {"shape", "ellipse"}},
|
2: {{"fontsize", "16"}, {"shape", "ellipse"}},
|
||||||
4: {},
|
4: {},
|
||||||
}),
|
}),
|
||||||
@@ -829,7 +830,7 @@ var encodeTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
g: undirectedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]Attribute{
|
g: undirectedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{
|
||||||
0: nil,
|
0: nil,
|
||||||
1: nil,
|
1: nil,
|
||||||
2: {{"fontsize", "16"}, {"shape", "ellipse"}},
|
2: {{"fontsize", "16"}, {"shape", "ellipse"}},
|
||||||
@@ -903,7 +904,7 @@ var encodeTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
g: directedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]Attribute{
|
g: directedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{
|
||||||
{from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}},
|
{from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}},
|
||||||
{from: 2, to: 4}: {},
|
{from: 2, to: 4}: {},
|
||||||
{from: 3, to: 4}: {{"color", "red"}},
|
{from: 3, to: 4}: {{"color", "red"}},
|
||||||
@@ -931,7 +932,7 @@ var encodeTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
g: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]Attribute{
|
g: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{
|
||||||
{from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}},
|
{from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}},
|
||||||
{from: 2, to: 4}: {},
|
{from: 2, to: 4}: {},
|
||||||
{from: 3, to: 4}: {{"color", "red"}},
|
{from: 3, to: 4}: {{"color", "red"}},
|
||||||
@@ -1004,7 +1005,7 @@ var encodeTests = []struct {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
g: directedPortedAttrGraphFrom(powerMethodGraph,
|
g: directedPortedAttrGraphFrom(powerMethodGraph,
|
||||||
[][]Attribute{
|
[][]encoding.Attribute{
|
||||||
2: {{"shape", "record"}, {"label", `"<Two>English|<Zwei>German"`}},
|
2: {{"shape", "record"}, {"label", `"<Two>English|<Zwei>German"`}},
|
||||||
4: {{"shape", "record"}, {"label", `"<Four>English|<Vier>German"`}},
|
4: {{"shape", "record"}, {"label", `"<Four>English|<Vier>German"`}},
|
||||||
},
|
},
|
||||||
@@ -1044,7 +1045,7 @@ var encodeTests = []struct {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
g: undirectedPortedAttrGraphFrom(powerMethodGraph,
|
g: undirectedPortedAttrGraphFrom(powerMethodGraph,
|
||||||
[][]Attribute{
|
[][]encoding.Attribute{
|
||||||
2: {{"shape", "record"}, {"label", `"<Two>English|<Zwei>German"`}},
|
2: {{"shape", "record"}, {"label", `"<Two>English|<Zwei>German"`}},
|
||||||
4: {{"shape", "record"}, {"label", `"<Four>English|<Vier>German"`}},
|
4: {{"shape", "record"}, {"label", `"<Four>English|<Vier>German"`}},
|
||||||
},
|
},
|
||||||
@@ -1092,7 +1093,7 @@ var encodeTests = []struct {
|
|||||||
|
|
||||||
// Handling graph attributes.
|
// Handling graph attributes.
|
||||||
{
|
{
|
||||||
g: graphAttributer{Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]Attribute{
|
g: graphAttributer{Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{
|
||||||
{from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}},
|
{from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}},
|
||||||
{from: 2, to: 4}: {},
|
{from: 2, to: 4}: {},
|
||||||
{from: 3, to: 4}: {{"color", "red"}},
|
{from: 3, to: 4}: {{"color", "red"}},
|
||||||
@@ -1120,13 +1121,13 @@ var encodeTests = []struct {
|
|||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
g: graphAttributer{Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]Attribute{
|
g: graphAttributer{Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{
|
||||||
{from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}},
|
{from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}},
|
||||||
{from: 2, to: 4}: {},
|
{from: 2, to: 4}: {},
|
||||||
{from: 3, to: 4}: {{"color", "red"}},
|
{from: 3, to: 4}: {{"color", "red"}},
|
||||||
}),
|
}),
|
||||||
graph: []Attribute{{"rankdir", `"LR"`}},
|
graph: []encoding.Attribute{{"rankdir", `"LR"`}},
|
||||||
node: []Attribute{{"fontsize", "16"}, {"shape", "ellipse"}},
|
node: []encoding.Attribute{{"fontsize", "16"}, {"shape", "ellipse"}},
|
||||||
},
|
},
|
||||||
|
|
||||||
want: `graph {
|
want: `graph {
|
||||||
|
35
graph/encoding/encoding.go
Normal file
35
graph/encoding/encoding.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright ©2017 The gonum Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package encoding provides a common graph encoding API.
|
||||||
|
package encoding // import "gonum.org/v1/gonum/graph/encoding"
|
||||||
|
|
||||||
|
import "gonum.org/v1/gonum/graph"
|
||||||
|
|
||||||
|
// Builder is a graph that can have user-defined nodes and edges added.
|
||||||
|
type Builder interface {
|
||||||
|
graph.Graph
|
||||||
|
graph.Builder
|
||||||
|
// NewEdge adds a new edge from the source to the destination node to the
|
||||||
|
// graph, or returns the existing edge if already present.
|
||||||
|
NewEdge(from, to graph.Node) graph.Edge
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalerAttr is implemented by types that can unmarshal a graph
|
||||||
|
// attribute description of themselves.
|
||||||
|
type UnmarshalerAttr interface {
|
||||||
|
// UnmarshalAttr decodes a single attribute.
|
||||||
|
UnmarshalAttr(attr Attribute) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attributer defines graph.Node or graph.Edge values that can
|
||||||
|
// specify graph attributes.
|
||||||
|
type Attributer interface {
|
||||||
|
Attributes() []Attribute
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute is an encoded key value attribute pair use in graph encoding.
|
||||||
|
type Attribute struct {
|
||||||
|
Key, Value string
|
||||||
|
}
|
Reference in New Issue
Block a user