Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 83 additions & 40 deletions node.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ type indentation struct {
level int
hasChild bool
indent string
w io.Writer
w io.Writer
}

func newIndentation(indent string, w io.Writer) *indentation {
Expand All @@ -168,101 +168,139 @@ func newIndentation(indent string, w io.Writer) *indentation {
}
}

func (i *indentation) NewLine() {
func (i *indentation) NewLine() (err error) {
if i == nil {
return
}
io.WriteString(i.w, "\n")
_, err = io.WriteString(i.w, "\n")
return
}

func (i *indentation) Open() {
func (i *indentation) Open() (err error) {
if i == nil {
return
}

io.WriteString(i.w, "\n")
io.WriteString(i.w, strings.Repeat(i.indent, i.level))
if err = i.writeIndent(); err != nil {
return
}

i.level++
i.hasChild = false
return
}

func (i *indentation) Close() {
func (i *indentation) Close() (err error) {
if i == nil {
return
}
i.level--
if i.hasChild {
io.WriteString(i.w, "\n")
io.WriteString(i.w, strings.Repeat(i.indent, i.level))
if err = i.writeIndent(); err != nil {
return
}
}
i.hasChild = true
return
}

func (i *indentation) writeIndent() (err error) {
_, err = io.WriteString(i.w, "\n")
if err != nil {
return
}
_, err = io.WriteString(i.w, strings.Repeat(i.indent, i.level))
return
}

func outputXML(w io.Writer, n *Node, preserveSpaces bool, config *outputConfiguration, indent *indentation) {
func outputXML(w io.Writer, n *Node, preserveSpaces bool, config *outputConfiguration, indent *indentation) (err error) {
preserveSpaces = calculatePreserveSpaces(n, preserveSpaces)
switch n.Type {
case TextNode:
io.WriteString(w, html.EscapeString(n.sanitizedData(preserveSpaces)))
_, err = io.WriteString(w, html.EscapeString(n.sanitizedData(preserveSpaces)))
return
case CharDataNode:
io.WriteString(w, "<![CDATA[")
io.WriteString(w, n.Data)
io.WriteString(w, "]]>")
_, err = fmt.Fprintf(w, "<![CDATA[%v]]>", n.Data)
return
case CommentNode:
if !config.skipComments {
io.WriteString(w, "<!--")
io.WriteString(w, n.Data)
io.WriteString(w, "-->")
_, err = fmt.Fprintf(w, "<!--%v-->", n.Data)
}
return
case NotationNode:
indent.NewLine()
fmt.Fprintf(w, "<!%s>", n.Data)
if err = indent.NewLine(); err != nil {
return
}
_, err = fmt.Fprintf(w, "<!%s>", n.Data)
return
case DeclarationNode:
io.WriteString(w, "<?" + n.Data)
_, err = io.WriteString(w, "<?"+n.Data)
if err != nil {
return
}
default:
indent.Open()
if err = indent.Open(); err != nil {
return
}
if n.Prefix == "" {
io.WriteString(w, "<" + n.Data)
_, err = io.WriteString(w, "<"+n.Data)
} else {
fmt.Fprintf(w, "<%s:%s", n.Prefix, n.Data)
_, err = fmt.Fprintf(w, "<%s:%s", n.Prefix, n.Data)
}
if err != nil {
return
}
}

for _, attr := range n.Attr {
if attr.Name.Space != "" {
fmt.Fprintf(w, ` %s:%s=`, attr.Name.Space, attr.Name.Local)
_, err = fmt.Fprintf(w, ` %s:%s=`, attr.Name.Space, attr.Name.Local)
} else {
fmt.Fprintf(w, ` %s=`, attr.Name.Local)
_, err = fmt.Fprintf(w, ` %s=`, attr.Name.Local)
}
if err != nil {
return
}

fmt.Fprintf(w, `"%v"`, html.EscapeString(attr.Value))
_, err = fmt.Fprintf(w, `"%v"`, html.EscapeString(attr.Value))
if err != nil {
return
}
}
if n.Type == DeclarationNode {
io.WriteString(w, "?>")
_, err = io.WriteString(w, "?>")
} else {
if n.FirstChild != nil || !config.emptyElementTagSupport {
io.WriteString(w, ">")
_, err = io.WriteString(w, ">")
} else {
io.WriteString(w, "/>")
indent.Close()
_, err = io.WriteString(w, "/>")
if err != nil {
return
}
err = indent.Close()
return
}
}
if err != nil {
return
}
for child := n.FirstChild; child != nil; child = child.NextSibling {
outputXML(w, child, preserveSpaces, config, indent)
err = outputXML(w, child, preserveSpaces, config, indent)
if err != nil {
return
}
}
if n.Type != DeclarationNode {
indent.Close()
if err = indent.Close(); err != nil {
return
}
if n.Prefix == "" {
fmt.Fprintf(w, "</%s>", n.Data)
_, err = fmt.Fprintf(w, "</%s>", n.Data)
} else {
fmt.Fprintf(w, "</%s:%s>", n.Prefix, n.Data)
_, err = fmt.Fprintf(w, "</%s:%s>", n.Prefix, n.Data)
}
}
return
}

// OutputXML returns the text that including tags name.
Expand All @@ -281,15 +319,15 @@ func (n *Node) OutputXMLWithOptions(opts ...OutputOption) string {
}

// Write writes xml to given writer.
func (n *Node) Write(writer io.Writer, self bool) {
func (n *Node) Write(writer io.Writer, self bool) error {
if self {
n.WriteWithOptions(writer, WithOutputSelf())
return n.WriteWithOptions(writer, WithOutputSelf())
}
n.WriteWithOptions(writer)
return n.WriteWithOptions(writer)
}

// WriteWithOptions writes xml with given options to given writer.
func (n *Node) WriteWithOptions(writer io.Writer, opts ...OutputOption) {
func (n *Node) WriteWithOptions(writer io.Writer, opts ...OutputOption) (err error) {
config := &outputConfiguration{}
// Set the options
for _, opt := range opts {
Expand All @@ -300,13 +338,18 @@ func (n *Node) WriteWithOptions(writer io.Writer, opts ...OutputOption) {
b := bufio.NewWriter(writer)
defer b.Flush()

ident := newIndentation(config.useIndentation, b)
if config.printSelf && n.Type != DocumentNode {
outputXML(b, n, preserveSpaces, config, newIndentation(config.useIndentation, b))
err = outputXML(b, n, preserveSpaces, config, ident)
} else {
for n := n.FirstChild; n != nil; n = n.NextSibling {
outputXML(b, n, preserveSpaces, config, newIndentation(config.useIndentation, b))
err = outputXML(b, n, preserveSpaces, config, ident)
if err != nil {
break
}
}
}
return
}

// AddAttr adds a new attribute specified by 'key' and 'val' to a node 'n'.
Expand Down
Loading