/
conref.go
131 lines (107 loc) · 3.13 KB
/
conref.go
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
128
129
130
131
package ditaconvert
import (
"bytes"
"encoding/xml"
"errors"
"fmt"
"io"
"path"
"strings"
)
func SameRootElement(a, b string) bool {
return strings.EqualFold(path.Dir(a), path.Dir(b))
}
func (context *Context) ResolveKeyRef(keyref string) (abspath, itempath string) {
if keyref == "" {
return "", ""
}
items := strings.SplitN(keyref, "/", 2)
if len(items) < 2 {
context.errorf("invalid conkeyref %v", keyref)
return "", ""
}
abspath, ok := context.Index.KeyDef[items[0]]
if !ok {
context.errorf("keydef missing for %v (%v)", items[0], keyref)
return "", ""
}
return abspath, items[1]
}
func (context *Context) HandleConref(dec *xml.Decoder, start xml.StartElement) error {
dec.Skip()
conref, conkeyref, conrefend := getAttr(&start, "conref"), getAttr(&start, "conkeyref"), getAttr(&start, "conrefend")
keyfile, keypath := context.ResolveKeyRef(conkeyref)
startfile, startpath := SplitLink(conref)
endfile, endpath := SplitLink(conrefend)
// startfile and endfile are relative to current direcotry
// keyfile is absolute relative to the root
if startfile != "" {
startfile = path.Join(path.Dir(context.DecodingPath), startfile)
}
if endfile != "" {
endfile = path.Join(path.Dir(context.DecodingPath), endfile)
}
// conref is missing, try to use conkeyref instead
if startfile == "" && keyfile != "" {
if startpath != "" || endpath != "" {
return errors.New("invalid conkeyref setup")
}
startfile, startpath = keyfile, keypath
}
// conrefend is missing, fallback to either conref or conkeyref
if endfile == "" && endpath == "" {
endfile, endpath = startfile, startpath
}
// start/end files are both missing, use the current file
if startfile == "" && endfile == "" {
startfile, endfile = context.DecodingPath, context.DecodingPath
}
// sanity check
if startfile != endfile {
return errors.New("conref and conrefend are in different files: " + startfile + " --> " + endfile)
}
if !SameRootElement(startpath, endpath) {
return errors.New("conref and conrefend have different root elements: " + conref + " --> " + conrefend)
}
if startpath == "" || endpath == "" {
return errors.New("invalid conref path: " + conref + " --> " + conrefend)
}
previousPath := context.DecodingPath
defer func() {
context.DecodingPath = previousPath
}()
data, _, err := context.Index.ReadFile(startfile)
if err != nil {
return fmt.Errorf("problem opening %v: %v", startfile, err)
}
subdec := xml.NewDecoder(bytes.NewReader(data))
subfirst, err := WalkNodePath(subdec, startpath)
if err != nil {
if err == io.EOF {
return errors.New("did not find conref: " + conref)
}
return err
}
var subtoken xml.Token = subfirst
endingid := path.Base(endpath)
for {
err := context.Handle(subdec, subtoken)
if err != nil {
return err
}
// is it ending?
if substart, isStart := subtoken.(xml.StartElement); isStart {
if strings.EqualFold(endingid, getAttr(&substart, "id")) {
return nil
}
}
if _, isEnd := subtoken.(xml.EndElement); isEnd {
return errors.New("did not find conrefend: " + conrefend)
}
subtoken, err = subdec.Token()
if err != nil {
return err
}
}
return nil
}