Added document and element Copy() functions.
Allows deep (recursive) copies of element trees.
diff --git a/etree.go b/etree.go
index 444fb0f..d71a264 100644
--- a/etree.go
+++ b/etree.go
@@ -28,6 +28,7 @@
// A Token is an empty interface that represents an Element,
// Comment, CharData, or ProcInst.
type Token interface {
+ dup(parent *Element) Token
writeTo(w *bufio.Writer)
}
@@ -87,6 +88,11 @@
return &Document{Element{Child: make([]Token, 0)}}
}
+// Copy returns a recursive, deep copy of the document.
+func (doc *Document) Copy() *Document {
+ return &Document{*(doc.dup(nil).(*Element))}
+}
+
// ReadFrom reads XML from the reader r into the document d.
// It returns the number of bytes read and any error encountered.
func (d *Document) ReadFrom(r io.Reader) (n int64, err error) {
@@ -184,6 +190,15 @@
d.Element.indent(0, indent)
}
+// Copy creates a parentless, recursive, deep copy of the element and
+// all its attributes and children. The returned element has no
+// parent but can be parented to a document using the CreateDocument
+// function.
+func (e *Element) Copy() *Element {
+ var parent *Element
+ return e.dup(parent).(*Element)
+}
+
// Text returns the characters immediately following the element's
// opening tag.
func (e *Element) Text() string {
@@ -470,6 +485,24 @@
e.Child = newChild
}
+// dup duplicates the element.
+func (e *Element) dup(parent *Element) Token {
+ ne := &Element{
+ Space: e.Space,
+ Tag: e.Tag,
+ Attr: make([]Attr, len(e.Attr)),
+ Child: make([]Token, len(e.Child)),
+ Parent: parent,
+ }
+ for i, t := range e.Child {
+ ne.Child[i] = t.dup(ne)
+ }
+ for i, a := range e.Attr {
+ ne.Attr[i] = a
+ }
+ return ne
+}
+
// writeTo serializes the element to the writer w.
func (e *Element) writeTo(w *bufio.Writer) {
w.WriteByte('<')
@@ -594,6 +627,11 @@
return c
}
+// dup duplicates the character data.
+func (c *CharData) dup(parent *Element) Token {
+ return newCharData(c.Data, c.whitespace)
+}
+
// writeTo serializes the character data entity to the writer.
func (c *CharData) writeTo(w *bufio.Writer) {
w.WriteString(escape(c.Data))
@@ -612,6 +650,11 @@
return c
}
+// dup duplicates the comment.
+func (c *Comment) dup(parent *Element) Token {
+ return newComment(c.Data)
+}
+
// writeTo serialies the comment to the writer.
func (c *Comment) writeTo(w *bufio.Writer) {
w.WriteString("<!--")
@@ -632,6 +675,11 @@
return d
}
+// dup duplicates the directive.
+func (d *Directive) dup(parent *Element) Token {
+ return newDirective(d.Data)
+}
+
// writeTo serializes the XML directive to the writer.
func (d *Directive) writeTo(w *bufio.Writer) {
w.WriteString("<!")
@@ -652,6 +700,11 @@
return p
}
+// dup duplicates the procinst.
+func (p *ProcInst) dup(parent *Element) Token {
+ return newProcInst(p.Target, p.Inst)
+}
+
// writeTo serializes the processing instruction to the writer.
func (p *ProcInst) writeTo(w *bufio.Writer) {
w.WriteString("<?")
diff --git a/etree_test.go b/etree_test.go
index fb38905..14b94d2 100644
--- a/etree_test.go
+++ b/etree_test.go
@@ -137,6 +137,52 @@
}
}
+func TestCopy(t *testing.T) {
+ s := `<store>
+ <book lang="en">
+ <title>Great Expectations</title>
+ <author>Charles Dickens</author>
+ </book>
+</store>`
+
+ doc1 := NewDocument()
+ err := doc1.ReadFromString(s)
+ if err != nil {
+ t.Fail()
+ }
+
+ s1, err := doc1.WriteToString()
+ if err != nil {
+ t.Fail()
+ }
+
+ doc2 := doc1.Copy()
+ s2, err := doc2.WriteToString()
+ if err != nil {
+ t.Fail()
+ }
+
+ if s1 != s2 {
+ t.Error("Copied documents don't match")
+ }
+
+ e1 := doc1.FindElement("./store/book/title")
+ e2 := doc2.FindElement("./store/book/title")
+ if e1 == nil || e2 == nil {
+ t.Error("Failed to find element")
+ }
+ if e1 == e2 {
+ t.Error("Copied documents contain same element")
+ }
+
+ e1.Parent.RemoveElement(e1)
+ s1, _ = doc1.WriteToString()
+ s2, _ = doc2.WriteToString()
+ if s1 == s2 {
+ t.Error("Copied and modified documents should not match")
+ }
+}
+
func compareElements(a []*Element, b []*Element) bool {
if len(a) != len(b) {
return true