-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathtree.go
More file actions
127 lines (101 loc) · 2.76 KB
/
tree.go
File metadata and controls
127 lines (101 loc) · 2.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package triplestore
import (
"fmt"
"sort"
)
// A tree is defined from a RDF Graph
// when given a specific predicate as an edge and
// considering triples pointing to RDF resource Object
//
// The tree defined by the graph/predicate should have no cycles
// and node should have at most one parent
type Tree struct {
g RDFGraph
predicate string
}
func NewTree(g RDFGraph, pred string) *Tree {
if g == nil {
panic("given RDF graph is nil")
}
return &Tree{g: g, predicate: pred}
}
// Traverse the tree in pre-order depth first search
func (t *Tree) TraverseDFS(node string, each func(RDFGraph, string, int) error, depths ...int) error {
var depth int
if len(depths) > 0 {
depth = depths[0]
}
if err := each(t.g, node, depth); err != nil {
return err
}
triples := t.g.WithSubjPred(node, t.predicate)
var childs []string
for _, tri := range triples {
n, ok := tri.Object().Resource()
if !ok {
return fmt.Errorf("object is not a resource identifier")
}
childs = append(childs, n)
}
sort.Strings(childs)
for _, child := range childs {
t.TraverseDFS(child, each, depth+1)
}
return nil
}
// Traverse all ancestors from the given node
func (t *Tree) TraverseAncestors(node string, each func(RDFGraph, string, int) error, depths ...int) error {
var depth int
if len(depths) > 0 {
depth = depths[0]
}
if err := each(t.g, node, depth); err != nil {
return err
}
triples := t.g.WithPredObj(t.predicate, Resource(node))
var parents []string
for _, tri := range triples {
parents = append(parents, tri.Subject())
}
sort.Strings(parents)
for _, parent := range parents {
t.TraverseAncestors(parent, each, depth+1)
}
return nil
}
// Traverse siblings of given node. Passed function allow to output the sibling criteria
func (t *Tree) TraverseSiblings(node string, siblingCriteriaFunc func(RDFGraph, string) (string, error), each func(RDFGraph, string, int) error) error {
triples := t.g.WithPredObj(t.predicate, Resource(node))
if len(triples) == 0 {
return each(t.g, node, 0)
}
if len(triples) != 1 {
return fmt.Errorf("tree[%s]: node %s with more than 1 parent: %v", t.predicate, node, triples)
}
otherChildTriples := t.g.WithSubjPred(triples[0].Subject(), t.predicate)
var childs []string
for _, c := range otherChildTriples {
child, ok := c.Object().Resource()
if !ok {
return fmt.Errorf("object is not a resource identifier")
}
childs = append(childs, child)
}
sort.Strings(childs)
nodeCriteria, err := siblingCriteriaFunc(t.g, node)
if err != nil {
return err
}
for _, child := range childs {
childCriteria, err := siblingCriteriaFunc(t.g, child)
if err != nil {
return err
}
if nodeCriteria == childCriteria {
if err := each(t.g, child, 0); err != nil {
return err
}
}
}
return nil
}