200 lines
4.5 KiB
Go
200 lines
4.5 KiB
Go
package dom
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"bytes"
|
|
)
|
|
|
|
type Attr struct {
|
|
Name xml.Name // Attribute namespace and name.
|
|
Value string // Attribute value.
|
|
}
|
|
|
|
type Element struct {
|
|
name xml.Name
|
|
children []*Element
|
|
parent *Element
|
|
content string
|
|
attributes []*Attr
|
|
namespaces []*Namespace
|
|
document *Document
|
|
}
|
|
|
|
func CreateElement(n string) *Element {
|
|
element := &Element { name: xml.Name { Local: n } }
|
|
element.children = make([]*Element, 0, 5)
|
|
element.attributes = make([]*Attr, 0, 10)
|
|
element.namespaces = make([]*Namespace, 0, 10)
|
|
return element
|
|
}
|
|
|
|
func (node *Element) AddChild(child *Element) *Element {
|
|
if child.parent != nil {
|
|
child.parent.RemoveChild(child)
|
|
}
|
|
child.SetParent(node)
|
|
node.children = append(node.children, child)
|
|
return node
|
|
}
|
|
|
|
func (node *Element) RemoveChild(child *Element) *Element {
|
|
p := -1
|
|
for i, v := range node.children {
|
|
if v == child {
|
|
p = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if p == -1 {
|
|
return node
|
|
}
|
|
|
|
copy(node.children[p:], node.children[p+1:])
|
|
node.children = node.children[0 : len(node.children)-1]
|
|
child.parent = nil
|
|
return node
|
|
}
|
|
|
|
func (node *Element) SetAttr(name string, value string) *Element {
|
|
// namespaces?
|
|
attr := &Attr{ Name: xml.Name { Local: name }, Value: value }
|
|
node.attributes = append(node.attributes, attr)
|
|
return node
|
|
}
|
|
|
|
func (node *Element) SetParent(parent *Element) *Element {
|
|
node.parent = parent
|
|
return node
|
|
}
|
|
|
|
func (node *Element) SetContent(content string) *Element {
|
|
node.content = content
|
|
return node
|
|
}
|
|
|
|
// Add a namespace declaration to this node
|
|
func (node *Element) DeclareNamespace(ns Namespace) *Element {
|
|
// check if we already have it
|
|
prefix := node.namespacePrefix(ns.Uri)
|
|
if prefix == ns.Prefix {
|
|
return node
|
|
}
|
|
// add it
|
|
node.namespaces = append(node.namespaces, &ns)
|
|
return node
|
|
}
|
|
|
|
func (node *Element) DeclaredNamespaces() []*Namespace {
|
|
return node.namespaces
|
|
}
|
|
|
|
func (node *Element) SetNamespace(prefix string, uri string) {
|
|
resolved := node.namespacePrefix(uri)
|
|
if resolved == "" {
|
|
// we couldn't find the namespace, let's declare it at this node
|
|
node.namespaces = append(node.namespaces, &Namespace { Prefix: prefix, Uri: uri })
|
|
}
|
|
node.name.Space = uri
|
|
}
|
|
|
|
func (node *Element) Bytes(out *bytes.Buffer, indent bool, indentType string, level int) {
|
|
empty := len(node.children) == 0 && node.content == ""
|
|
content := node.content != ""
|
|
// children := len(node.children) > 0
|
|
// ns := len(node.namespaces) > 0
|
|
// attrs := len(node.attributes) > 0
|
|
|
|
indentStr := ""
|
|
nextLine := ""
|
|
if indent {
|
|
nextLine = "\n"
|
|
for i := 0; i < level; i++ {
|
|
indentStr += indentType
|
|
}
|
|
}
|
|
|
|
if node.name.Local != "" {
|
|
if len(node.name.Space) > 0 {
|
|
// first find if ns has been declared, otherwise
|
|
prefix := node.namespacePrefix(node.name.Space)
|
|
fmt.Fprintf(out, "%s<%s:%s", indentStr, prefix, node.name.Local)
|
|
} else {
|
|
fmt.Fprintf(out, "%s<%s", indentStr, node.name.Local)
|
|
}
|
|
}
|
|
|
|
// declared namespaces
|
|
for _, v := range node.namespaces {
|
|
prefix := node.namespacePrefix(v.Uri)
|
|
fmt.Fprintf(out, ` xmlns:%s="%s"`, prefix, v.Uri)
|
|
}
|
|
|
|
// attributes
|
|
for _, v := range node.attributes {
|
|
if len(v.Name.Space) > 0 {
|
|
prefix := node.namespacePrefix(v.Name.Space)
|
|
fmt.Fprintf(out, ` %s:%s="%s"`, prefix, v.Name.Local, v.Value)
|
|
} else {
|
|
fmt.Fprintf(out, ` %s="%s"`, v.Name.Local, v.Value)
|
|
}
|
|
}
|
|
|
|
// close tag
|
|
if empty {
|
|
fmt.Fprintf(out, "/>%s", nextLine)
|
|
} else {
|
|
if content {
|
|
out.WriteRune('>')
|
|
} else {
|
|
fmt.Fprintf(out, ">%s", nextLine)
|
|
}
|
|
}
|
|
|
|
if len(node.children) > 0 {
|
|
for _, child := range node.children {
|
|
child.Bytes(out, indent, indentType, level + 1)
|
|
}
|
|
} else if node.content != "" {
|
|
//val := []byte(node.content)
|
|
//xml.EscapeText(out, val)
|
|
out.WriteString(node.content)
|
|
}
|
|
|
|
if !empty && len(node.name.Local) > 0 {
|
|
var indentation string
|
|
if content {
|
|
indentation = ""
|
|
} else {
|
|
indentation = indentStr
|
|
}
|
|
if len(node.name.Space) > 0 {
|
|
prefix := node.namespacePrefix(node.name.Space)
|
|
fmt.Fprintf(out, "%s</%s:%s>\n", indentation, prefix, node.name.Local)
|
|
} else {
|
|
fmt.Fprintf(out, "%s</%s>\n", indentation, node.name.Local)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Finds the prefix of the given namespace if it has been declared
|
|
// in this node or in one of its parent
|
|
func (node *Element) namespacePrefix(uri string) string {
|
|
for _, ns := range node.namespaces {
|
|
if ns.Uri == uri {
|
|
return ns.Prefix
|
|
}
|
|
}
|
|
if node.parent == nil {
|
|
return ""
|
|
}
|
|
return node.parent.namespacePrefix(uri)
|
|
}
|
|
|
|
|
|
func (node *Element) String() string {
|
|
var b bytes.Buffer
|
|
node.Bytes(&b, false, "", 0)
|
|
return string(b.Bytes())
|
|
} |