-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathnode_base.go
More file actions
100 lines (88 loc) · 2.45 KB
/
node_base.go
File metadata and controls
100 lines (88 loc) · 2.45 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
package helium
import (
"net/url"
"path/filepath"
"strings"
"github.com/lestrrat-go/helium/internal/lexicon"
)
// NodeGetBase returns the effective base URI for a node by walking ancestors
// and resolving xml:base attributes. Returns empty string if no base URI found.
func NodeGetBase(doc *Document, n Node) string {
if n == nil {
return ""
}
// Collect xml:base values from the node up to the root.
var bases []string
for cur := n; cur != nil; cur = cur.Parent() {
if elem, ok := AsNode[*Element](cur); ok {
if val, ok := elem.GetAttributeNS("base", lexicon.NamespaceXML); ok && val != "" {
bases = append(bases, val)
}
}
// If an ancestor has an entity base URI, use it as the base
// instead of continuing up to the document.
if ebu := cur.baseDocNode().entityBaseURI; ebu != "" {
// Resolve from outermost to innermost, starting from the
// entity base URI instead of the document URL.
base := ebu
for i := len(bases) - 1; i >= 0; i-- {
base = BuildURI(bases[i], base)
}
return base
}
}
// Use the document's URL as the starting base, if available.
var base string
if doc != nil && doc.url != "" {
base = doc.url
}
// Resolve from outermost ancestor inward (reverse order).
for i := len(bases) - 1; i >= 0; i-- {
if base == "" {
base = bases[i]
} else {
base = BuildURI(bases[i], base)
}
}
return base
}
// SetNodeBaseURI sets an explicit base URI on a node. This is used by
// xsl:copy to preserve the original element's base URI on the copy.
func SetNodeBaseURI(n Node, uri string) {
n.baseDocNode().entityBaseURI = uri
}
// BuildURI resolves a relative system ID against a base URI.
// For local file paths (no scheme or file: scheme), it uses filepath.Join.
// For other schemes, it uses url.ResolveReference.
func BuildURI(systemID, base string) string {
u, err := url.Parse(systemID)
if err != nil {
return ""
}
if u.IsAbs() {
return systemID
}
baseURL, err := url.Parse(base)
if err != nil {
return ""
}
if baseURL.Scheme != "" && baseURL.Scheme != "file" {
return baseURL.ResolveReference(u).String()
}
if filepath.IsAbs(systemID) {
return systemID
}
basePath := baseURL.Path
if basePath == "" {
basePath = base
}
dir := filepath.Dir(basePath)
if strings.HasSuffix(basePath, "/") {
dir = strings.TrimRight(basePath, "/")
}
result := filepath.Join(dir, systemID)
if strings.HasSuffix(systemID, "/") && !strings.HasSuffix(result, "/") {
result += "/"
}
return result
}