Skip to content

Commit

Permalink
Merge branch '#116'
Browse files Browse the repository at this point in the history
  • Loading branch information
zhengchun committed Oct 25, 2024
2 parents 046b64c + 388aef3 commit deda77b
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 6 deletions.
70 changes: 64 additions & 6 deletions node.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type outputConfiguration struct {
preserveSpaces bool
emptyElementTagSupport bool
skipComments bool
useIndentation string
}

type OutputOption func(*outputConfiguration)
Expand Down Expand Up @@ -89,6 +90,13 @@ func WithPreserveSpace() OutputOption {
}
}

// WithIndentation sets the indentation string used for formatting the output.
func WithIndentation(indentation string) OutputOption {
return func(oc *outputConfiguration) {
oc.useIndentation = indentation
}
}

func newXMLName(name string) xml.Name {
if i := strings.IndexByte(name, ':'); i > 0 {
return xml.Name{
Expand Down Expand Up @@ -141,7 +149,53 @@ func calculatePreserveSpaces(n *Node, pastValue bool) bool {
return pastValue
}

func outputXML(b *strings.Builder, n *Node, preserveSpaces bool, config *outputConfiguration) {
type indentation struct {
level int
hasChild bool
indent string
b *strings.Builder
}

func newIndentation(indent string, b *strings.Builder) *indentation {
if indent == "" {
return nil
}
return &indentation{
indent: indent,
b: b,
}
}

func (i *indentation) NewLine() {
if i == nil {
return
}
i.b.WriteString("\n")
}

func (i *indentation) Open() {
if i == nil {
return
}
i.b.WriteString("\n")
i.b.WriteString(strings.Repeat(i.indent, i.level))
i.level++
i.hasChild = false
}

func (i *indentation) Close() {
if i == nil {
return
}
i.level--
if i.hasChild {
i.b.WriteString("\n")
i.b.WriteString(strings.Repeat(i.indent, i.level))
}
i.hasChild = true
}

func outputXML(b *strings.Builder, n *Node, preserveSpaces bool, config *outputConfiguration, indent *indentation) {
preserveSpaces = calculatePreserveSpaces(n, preserveSpaces)
switch n.Type {
case TextNode:
Expand All @@ -160,11 +214,13 @@ func outputXML(b *strings.Builder, n *Node, preserveSpaces bool, config *outputC
}
return
case NotationNode:
indent.NewLine()
fmt.Fprintf(b, "<!%s>", n.Data)
return
case DeclarationNode:
b.WriteString("<?" + n.Data)
default:
indent.Open()
if n.Prefix == "" {
b.WriteString("<" + n.Data)
} else {
Expand All @@ -189,13 +245,15 @@ func outputXML(b *strings.Builder, n *Node, preserveSpaces bool, config *outputC
b.WriteString(">")
} else {
b.WriteString("/>")
indent.Close()
return
}
}
for child := n.FirstChild; child != nil; child = child.NextSibling {
outputXML(b, child, preserveSpaces, config)
outputXML(b, child, preserveSpaces, config, indent)
}
if n.Type != DeclarationNode {
indent.Close()
if n.Prefix == "" {
fmt.Fprintf(b, "</%s>", n.Data)
} else {
Expand All @@ -214,10 +272,10 @@ func (n *Node) OutputXML(self bool) string {
preserveSpaces := calculatePreserveSpaces(n, false)
var b strings.Builder
if self && n.Type != DocumentNode {
outputXML(&b, n, preserveSpaces, config)
outputXML(&b, n, preserveSpaces, config, newIndentation(config.useIndentation, &b))
} else {
for n := n.FirstChild; n != nil; n = n.NextSibling {
outputXML(&b, n, preserveSpaces, config)
outputXML(&b, n, preserveSpaces, config, newIndentation(config.useIndentation, &b))
}
}

Expand All @@ -236,10 +294,10 @@ func (n *Node) OutputXMLWithOptions(opts ...OutputOption) string {
preserveSpaces := calculatePreserveSpaces(n, pastPreserveSpaces)
var b strings.Builder
if config.printSelf && n.Type != DocumentNode {
outputXML(&b, n, preserveSpaces, config)
outputXML(&b, n, preserveSpaces, config, newIndentation(config.useIndentation, &b))
} else {
for n := n.FirstChild; n != nil; n = n.NextSibling {
outputXML(&b, n, preserveSpaces, config)
outputXML(&b, n, preserveSpaces, config, newIndentation(config.useIndentation, &b))
}
}

Expand Down
16 changes: 16 additions & 0 deletions node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,22 @@ func TestOutputXMLWithPreserveSpaceOption(t *testing.T) {
}
}

func TestOutputXMLWithIndentation(t *testing.T) {
s := `<?xml version="1.0" encoding="utf-8"?><xml><outerTag><tagWithValue>123</tagWithValue></outerTag></xml>`
expected := `<?xml version="1.0" encoding="utf-8"?>
<xml>
<outerTag>
<tagWithValue>123</tagWithValue>
</outerTag>
</xml>`

doc, _ := Parse(strings.NewReader(s))
resultWithIndent := doc.OutputXMLWithOptions(WithIndentation(" "))
if resultWithIndent != expected {
t.Errorf("output was not expected. expected %v but got %v", expected, resultWithIndent)
}
}

func TestNodeLevel(t *testing.T) {
s := `<?xml version="1.0" encoding="utf-8"?>
<class_list>
Expand Down

0 comments on commit deda77b

Please sign in to comment.