summaryrefslogtreecommitdiff
path: root/vendor/github.com/beevik/etree/etree.go
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2025-12-10 21:08:39 +0100
committerxengineering <me@xengineering.eu>2025-12-10 21:08:39 +0100
commit1f4c3308be296d95163e3e0ea54761410f4da140 (patch)
tree54640f0f423c02aaae07d99a6754beb47a83f79c /vendor/github.com/beevik/etree/etree.go
parent83277c420525e06f9d0c234e018f481b4579d6cd (diff)
downloadsia-server-1f4c3308be296d95163e3e0ea54761410f4da140.tar
sia-server-1f4c3308be296d95163e3e0ea54761410f4da140.tar.zst
sia-server-1f4c3308be296d95163e3e0ea54761410f4da140.zip
Add homematic-go v0.1.0
This is the minimal viable product (MVP) of this library suitable to build the MVP of the sia-server.
Diffstat (limited to 'vendor/github.com/beevik/etree/etree.go')
-rw-r--r--vendor/github.com/beevik/etree/etree.go1857
1 files changed, 1857 insertions, 0 deletions
diff --git a/vendor/github.com/beevik/etree/etree.go b/vendor/github.com/beevik/etree/etree.go
new file mode 100644
index 0000000..bfe1f06
--- /dev/null
+++ b/vendor/github.com/beevik/etree/etree.go
@@ -0,0 +1,1857 @@
+// Copyright 2015-2019 Brett Vickers.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package etree provides XML services through an Element Tree
+// abstraction.
+package etree
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/xml"
+ "errors"
+ "io"
+ "iter"
+ "os"
+ "slices"
+ "strings"
+)
+
+const (
+ // NoIndent is used with the IndentSettings record to remove all
+ // indenting.
+ NoIndent = -1
+)
+
+// ErrXML is returned when XML parsing fails due to incorrect formatting.
+var ErrXML = errors.New("etree: invalid XML format")
+
+// cdataPrefix is used to detect CDATA text when ReadSettings.PreserveCData is
+// true.
+var cdataPrefix = []byte("<![CDATA[")
+
+// ReadSettings determine the default behavior of the Document's ReadFrom*
+// functions.
+type ReadSettings struct {
+ // CharsetReader, if non-nil, defines a function to generate
+ // charset-conversion readers, converting from the provided non-UTF-8
+ // charset into UTF-8. If nil, the ReadFrom* functions will use a
+ // "pass-through" CharsetReader that performs no conversion on the reader's
+ // data regardless of the value of the "charset" encoding string. Default:
+ // nil.
+ CharsetReader func(charset string, input io.Reader) (io.Reader, error)
+
+ // Permissive allows input containing common mistakes such as missing tags
+ // or attribute values. Default: false.
+ Permissive bool
+
+ // Preserve CDATA character data blocks when decoding XML (instead of
+ // converting it to normal character text). This entails additional
+ // processing and memory usage during ReadFrom* operations. Default:
+ // false.
+ PreserveCData bool
+
+ // When an element has two or more attributes with the same name,
+ // preserve them instead of keeping only one. Default: false.
+ PreserveDuplicateAttrs bool
+
+ // ValidateInput forces all ReadFrom* functions to validate that the
+ // provided input is composed of "well-formed"(*) XML before processing it.
+ // If invalid XML is detected, the ReadFrom* functions return an error.
+ // Because this option requires the input to be processed twice, it incurs a
+ // significant performance penalty. Default: false.
+ //
+ // (*) Note that this definition of "well-formed" is in the context of the
+ // go standard library's encoding/xml package. Go's encoding/xml package
+ // does not, in fact, guarantee well-formed XML as specified by the W3C XML
+ // recommendation. See: https://github.com/golang/go/issues/68299
+ ValidateInput bool
+
+ // Entity to be passed to standard xml.Decoder. Default: nil.
+ Entity map[string]string
+
+ // When Permissive is true, AutoClose indicates a set of elements to
+ // consider closed immediately after they are opened, regardless of
+ // whether an end element is present. Commonly set to xml.HTMLAutoClose.
+ // Default: nil.
+ AutoClose []string
+}
+
+// defaultCharsetReader is used by the xml decoder when the ReadSettings
+// CharsetReader value is nil. It behaves as a "pass-through", ignoring
+// the requested charset parameter and skipping conversion altogether.
+func defaultCharsetReader(charset string, input io.Reader) (io.Reader, error) {
+ return input, nil
+}
+
+// dup creates a duplicate of the ReadSettings object.
+func (s *ReadSettings) dup() ReadSettings {
+ var entityCopy map[string]string
+ if s.Entity != nil {
+ entityCopy = make(map[string]string)
+ for k, v := range s.Entity {
+ entityCopy[k] = v
+ }
+ }
+ return ReadSettings{
+ CharsetReader: s.CharsetReader,
+ Permissive: s.Permissive,
+ Entity: entityCopy,
+ }
+}
+
+// WriteSettings determine the behavior of the Document's WriteTo* functions.
+type WriteSettings struct {
+ // CanonicalEndTags forces the production of XML end tags, even for
+ // elements that have no child elements. Default: false.
+ CanonicalEndTags bool
+
+ // CanonicalText forces the production of XML character references for
+ // text data characters &, <, and >. If false, XML character references
+ // are also produced for " and '. Default: false.
+ CanonicalText bool
+
+ // CanonicalAttrVal forces the production of XML character references for
+ // attribute value characters &, < and ". If false, XML character
+ // references are also produced for > and '. Ignored when AttrSingleQuote
+ // is true. Default: false.
+ CanonicalAttrVal bool
+
+ // AttrSingleQuote causes attributes to use single quotes (attr='example')
+ // instead of double quotes (attr = "example") when set to true. Default:
+ // false.
+ AttrSingleQuote bool
+
+ // UseCRLF causes the document's Indent* functions to use a carriage return
+ // followed by a linefeed ("\r\n") when outputting a newline. If false,
+ // only a linefeed is used ("\n"). Default: false.
+ //
+ // Deprecated: UseCRLF is deprecated. Use IndentSettings.UseCRLF instead.
+ UseCRLF bool
+}
+
+// dup creates a duplicate of the WriteSettings object.
+func (s *WriteSettings) dup() WriteSettings {
+ return *s
+}
+
+// IndentSettings determine the behavior of the Document's Indent* functions.
+type IndentSettings struct {
+ // Spaces indicates the number of spaces to insert for each level of
+ // indentation. Set to etree.NoIndent to remove all indentation. Ignored
+ // when UseTabs is true. Default: 4.
+ Spaces int
+
+ // UseTabs causes tabs to be used instead of spaces when indenting.
+ // Default: false.
+ UseTabs bool
+
+ // UseCRLF causes newlines to be written as a carriage return followed by
+ // a linefeed ("\r\n"). If false, only a linefeed character is output
+ // for a newline ("\n"). Default: false.
+ UseCRLF bool
+
+ // PreserveLeafWhitespace causes indent functions to preserve whitespace
+ // within XML elements containing only non-CDATA character data. Default:
+ // false.
+ PreserveLeafWhitespace bool
+
+ // SuppressTrailingWhitespace suppresses the generation of a trailing
+ // whitespace characters (such as newlines) at the end of the indented
+ // document. Default: false.
+ SuppressTrailingWhitespace bool
+}
+
+// NewIndentSettings creates a default IndentSettings record.
+func NewIndentSettings() *IndentSettings {
+ return &IndentSettings{
+ Spaces: 4,
+ UseTabs: false,
+ UseCRLF: false,
+ PreserveLeafWhitespace: false,
+ SuppressTrailingWhitespace: false,
+ }
+}
+
+type indentFunc func(depth int) string
+
+func getIndentFunc(s *IndentSettings) indentFunc {
+ if s.UseTabs {
+ if s.UseCRLF {
+ return func(depth int) string { return indentCRLF(depth, indentTabs) }
+ } else {
+ return func(depth int) string { return indentLF(depth, indentTabs) }
+ }
+ } else {
+ if s.Spaces < 0 {
+ return func(depth int) string { return "" }
+ } else if s.UseCRLF {
+ return func(depth int) string { return indentCRLF(depth*s.Spaces, indentSpaces) }
+ } else {
+ return func(depth int) string { return indentLF(depth*s.Spaces, indentSpaces) }
+ }
+ }
+}
+
+// Writer is the interface that wraps the Write* functions called by each token
+// type's WriteTo function.
+type Writer interface {
+ io.StringWriter
+ io.ByteWriter
+ io.Writer
+}
+
+// A Token is an interface type used to represent XML elements, character
+// data, CDATA sections, XML comments, XML directives, and XML processing
+// instructions.
+type Token interface {
+ Parent() *Element
+ Index() int
+ WriteTo(w Writer, s *WriteSettings)
+ dup(parent *Element) Token
+ setParent(parent *Element)
+ setIndex(index int)
+}
+
+// A Document is a container holding a complete XML tree.
+//
+// A document has a single embedded element, which contains zero or more child
+// tokens, one of which is usually the root element. The embedded element may
+// include other children such as processing instruction tokens or character
+// data tokens. The document's embedded element is never directly serialized;
+// only its children are.
+//
+// A document also contains read and write settings, which influence the way
+// the document is deserialized, serialized, and indented.
+type Document struct {
+ Element
+ ReadSettings ReadSettings
+ WriteSettings WriteSettings
+}
+
+// An Element represents an XML element, its attributes, and its child tokens.
+type Element struct {
+ Space, Tag string // namespace prefix and tag
+ Attr []Attr // key-value attribute pairs
+ Child []Token // child tokens (elements, comments, etc.)
+ parent *Element // parent element
+ index int // token index in parent's children
+}
+
+// An Attr represents a key-value attribute within an XML element.
+type Attr struct {
+ Space, Key string // The attribute's namespace prefix and key
+ Value string // The attribute value string
+ element *Element // element containing the attribute
+}
+
+// charDataFlags are used with CharData tokens to store additional settings.
+type charDataFlags uint8
+
+const (
+ // The CharData contains only whitespace.
+ whitespaceFlag charDataFlags = 1 << iota
+
+ // The CharData contains a CDATA section.
+ cdataFlag
+)
+
+// CharData may be used to represent simple text data or a CDATA section
+// within an XML document. The Data property should never be modified
+// directly; use the SetData function instead.
+type CharData struct {
+ Data string // the simple text or CDATA section content
+ parent *Element
+ index int
+ flags charDataFlags
+}
+
+// A Comment represents an XML comment.
+type Comment struct {
+ Data string // the comment's text
+ parent *Element
+ index int
+}
+
+// A Directive represents an XML directive.
+type Directive struct {
+ Data string // the directive string
+ parent *Element
+ index int
+}
+
+// A ProcInst represents an XML processing instruction.
+type ProcInst struct {
+ Target string // the processing instruction target
+ Inst string // the processing instruction value
+ parent *Element
+ index int
+}
+
+// NewDocument creates an XML document without a root element.
+func NewDocument() *Document {
+ return &Document{
+ Element: Element{Child: make([]Token, 0)},
+ }
+}
+
+// NewDocumentWithRoot creates an XML document and sets the element 'e' as its
+// root element. If the element 'e' is already part of another document, it is
+// first removed from its existing document.
+func NewDocumentWithRoot(e *Element) *Document {
+ d := NewDocument()
+ d.SetRoot(e)
+ return d
+}
+
+// Copy returns a recursive, deep copy of the document.
+func (d *Document) Copy() *Document {
+ return &Document{
+ Element: *(d.Element.dup(nil).(*Element)),
+ ReadSettings: d.ReadSettings.dup(),
+ WriteSettings: d.WriteSettings.dup(),
+ }
+}
+
+// Root returns the root element of the document. It returns nil if there is
+// no root element.
+func (d *Document) Root() *Element {
+ for _, t := range d.Child {
+ if c, ok := t.(*Element); ok {
+ return c
+ }
+ }
+ return nil
+}
+
+// SetRoot replaces the document's root element with the element 'e'. If the
+// document already has a root element when this function is called, then the
+// existing root element is unbound from the document. If the element 'e' is
+// part of another document, then it is unbound from the other document.
+func (d *Document) SetRoot(e *Element) {
+ if e.parent != nil {
+ e.parent.RemoveChild(e)
+ }
+
+ // If there is already a root element, replace it.
+ p := &d.Element
+ for i, t := range p.Child {
+ if _, ok := t.(*Element); ok {
+ t.setParent(nil)
+ t.setIndex(-1)
+ p.Child[i] = e
+ e.setParent(p)
+ e.setIndex(i)
+ return
+ }
+ }
+
+ // No existing root element, so add it.
+ p.addChild(e)
+}
+
+// ReadFrom reads XML from the reader 'r' into this document. The function
+// returns the number of bytes read and any error encountered.
+func (d *Document) ReadFrom(r io.Reader) (n int64, err error) {
+ if d.ReadSettings.ValidateInput {
+ b, err := io.ReadAll(r)
+ if err != nil {
+ return 0, err
+ }
+ if err := validateXML(bytes.NewReader(b), d.ReadSettings); err != nil {
+ return 0, err
+ }
+ r = bytes.NewReader(b)
+ }
+ return d.Element.readFrom(r, d.ReadSettings)
+}
+
+// ReadFromFile reads XML from a local file at path 'filepath' into this
+// document.
+func (d *Document) ReadFromFile(filepath string) error {
+ f, err := os.Open(filepath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ _, err = d.ReadFrom(f)
+ return err
+}
+
+// ReadFromBytes reads XML from the byte slice 'b' into the this document.
+func (d *Document) ReadFromBytes(b []byte) error {
+ if d.ReadSettings.ValidateInput {
+ if err := validateXML(bytes.NewReader(b), d.ReadSettings); err != nil {
+ return err
+ }
+ }
+ _, err := d.Element.readFrom(bytes.NewReader(b), d.ReadSettings)
+ return err
+}
+
+// ReadFromString reads XML from the string 's' into this document.
+func (d *Document) ReadFromString(s string) error {
+ if d.ReadSettings.ValidateInput {
+ if err := validateXML(strings.NewReader(s), d.ReadSettings); err != nil {
+ return err
+ }
+ }
+ _, err := d.Element.readFrom(strings.NewReader(s), d.ReadSettings)
+ return err
+}
+
+// validateXML determines if the data read from the reader 'r' contains
+// well-formed XML according to the rules set by the go xml package.
+func validateXML(r io.Reader, settings ReadSettings) error {
+ dec := newDecoder(r, settings)
+ err := dec.Decode(new(interface{}))
+ if err != nil {
+ return err
+ }
+
+ // If there are any trailing tokens after unmarshalling with Decode(),
+ // then the XML input didn't terminate properly.
+ _, err = dec.Token()
+ if err == io.EOF {
+ return nil
+ }
+ return ErrXML
+}
+
+// newDecoder creates an XML decoder for the reader 'r' configured using
+// the provided read settings.
+func newDecoder(r io.Reader, settings ReadSettings) *xml.Decoder {
+ d := xml.NewDecoder(r)
+ d.CharsetReader = settings.CharsetReader
+ if d.CharsetReader == nil {
+ d.CharsetReader = defaultCharsetReader
+ }
+ d.Strict = !settings.Permissive
+ d.Entity = settings.Entity
+ d.AutoClose = settings.AutoClose
+ return d
+}
+
+// WriteTo serializes the document out to the writer 'w'. The function returns
+// the number of bytes written and any error encountered.
+func (d *Document) WriteTo(w io.Writer) (n int64, err error) {
+ xw := newXmlWriter(w)
+ b := bufio.NewWriter(xw)
+ for _, c := range d.Child {
+ c.WriteTo(b, &d.WriteSettings)
+ }
+ err, n = b.Flush(), xw.bytes
+ return
+}
+
+// WriteToFile serializes the document out to the file at path 'filepath'.
+func (d *Document) WriteToFile(filepath string) error {
+ f, err := os.Create(filepath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+ _, err = d.WriteTo(f)
+ return err
+}
+
+// WriteToBytes serializes this document into a slice of bytes.
+func (d *Document) WriteToBytes() (b []byte, err error) {
+ var buf bytes.Buffer
+ if _, err = d.WriteTo(&buf); err != nil {
+ return
+ }
+ return buf.Bytes(), nil
+}
+
+// WriteToString serializes this document into a string.
+func (d *Document) WriteToString() (s string, err error) {
+ var b []byte
+ if b, err = d.WriteToBytes(); err != nil {
+ return
+ }
+ return string(b), nil
+}
+
+// Indent modifies the document's element tree by inserting character data
+// tokens containing newlines and spaces for indentation. The amount of
+// indentation per depth level is given by the 'spaces' parameter. Other than
+// the number of spaces, default IndentSettings are used.
+func (d *Document) Indent(spaces int) {
+ s := NewIndentSettings()
+ s.Spaces = spaces
+ d.IndentWithSettings(s)
+}
+
+// IndentTabs modifies the document's element tree by inserting CharData
+// tokens containing newlines and tabs for indentation. One tab is used per
+// indentation level. Other than the use of tabs, default IndentSettings
+// are used.
+func (d *Document) IndentTabs() {
+ s := NewIndentSettings()
+ s.UseTabs = true
+ d.IndentWithSettings(s)
+}
+
+// IndentWithSettings modifies the document's element tree by inserting
+// character data tokens containing newlines and indentation. The behavior
+// of the indentation algorithm is configured by the indent settings.
+func (d *Document) IndentWithSettings(s *IndentSettings) {
+ // WriteSettings.UseCRLF is deprecated. Until removed from the package, it
+ // overrides IndentSettings.UseCRLF when true.
+ if d.WriteSettings.UseCRLF {
+ s.UseCRLF = true
+ }
+
+ d.Element.indent(0, getIndentFunc(s), s)
+
+ if s.SuppressTrailingWhitespace {
+ d.Element.stripTrailingWhitespace()
+ }
+}
+
+// Unindent modifies the document's element tree by removing character data
+// tokens containing only whitespace. Other than the removal of indentation,
+// default IndentSettings are used.
+func (d *Document) Unindent() {
+ s := NewIndentSettings()
+ s.Spaces = NoIndent
+ d.IndentWithSettings(s)
+}
+
+// NewElement creates an unparented element with the specified tag (i.e.,
+// name). The tag may include a namespace prefix followed by a colon.
+func NewElement(tag string) *Element {
+ space, stag := spaceDecompose(tag)
+ return newElement(space, stag, nil)
+}
+
+// newElement is a helper function that creates an element and binds it to
+// a parent element if possible.
+func newElement(space, tag string, parent *Element) *Element {
+ e := &Element{
+ Space: space,
+ Tag: tag,
+ Attr: make([]Attr, 0),
+ Child: make([]Token, 0),
+ parent: parent,
+ index: -1,
+ }
+ if parent != nil {
+ parent.addChild(e)
+ }
+ return e
+}
+
+// Copy creates a recursive, deep copy of the element and all its attributes
+// and children. The returned element has no parent but can be parented to a
+// another element using AddChild, or added to a document with SetRoot or
+// NewDocumentWithRoot.
+func (e *Element) Copy() *Element {
+ return e.dup(nil).(*Element)
+}
+
+// FullTag returns the element e's complete tag, including namespace prefix if
+// present.
+func (e *Element) FullTag() string {
+ if e.Space == "" {
+ return e.Tag
+ }
+ return e.Space + ":" + e.Tag
+}
+
+// NamespaceURI returns the XML namespace URI associated with the element. If
+// the element is part of the XML default namespace, NamespaceURI returns the
+// empty string.
+func (e *Element) NamespaceURI() string {
+ if e.Space == "" {
+ return e.findDefaultNamespaceURI()
+ }
+ return e.findLocalNamespaceURI(e.Space)
+}
+
+// findLocalNamespaceURI finds the namespace URI corresponding to the
+// requested prefix.
+func (e *Element) findLocalNamespaceURI(prefix string) string {
+ for _, a := range e.Attr {
+ if a.Space == "xmlns" && a.Key == prefix {
+ return a.Value
+ }
+ }
+
+ if e.parent == nil {
+ return ""
+ }
+
+ return e.parent.findLocalNamespaceURI(prefix)
+}
+
+// findDefaultNamespaceURI finds the default namespace URI of the element.
+func (e *Element) findDefaultNamespaceURI() string {
+ for _, a := range e.Attr {
+ if a.Space == "" && a.Key == "xmlns" {
+ return a.Value
+ }
+ }
+
+ if e.parent == nil {
+ return ""
+ }
+
+ return e.parent.findDefaultNamespaceURI()
+}
+
+// namespacePrefix returns the namespace prefix associated with the element.
+func (e *Element) namespacePrefix() string {
+ return e.Space
+}
+
+// name returns the tag associated with the element.
+func (e *Element) name() string {
+ return e.Tag
+}
+
+// ReindexChildren recalculates the index values of the element's child
+// tokens. This is necessary only if you have manually manipulated the
+// element's `Child` array.
+func (e *Element) ReindexChildren() {
+ for i := 0; i < len(e.Child); i++ {
+ e.Child[i].setIndex(i)
+ }
+}
+
+// Text returns all character data immediately following the element's opening
+// tag.
+func (e *Element) Text() string {
+ if len(e.Child) == 0 {
+ return ""
+ }
+
+ text := ""
+ for _, ch := range e.Child {
+ if cd, ok := ch.(*CharData); ok {
+ if text == "" {
+ text = cd.Data
+ } else {
+ text += cd.Data
+ }
+ } else if _, ok := ch.(*Comment); ok {
+ // ignore
+ } else {
+ break
+ }
+ }
+ return text
+}
+
+// SetText replaces all character data immediately following an element's
+// opening tag with the requested string.
+func (e *Element) SetText(text string) {
+ e.replaceText(0, text, 0)
+}
+
+// SetCData replaces all character data immediately following an element's
+// opening tag with a CDATA section.
+func (e *Element) SetCData(text string) {
+ e.replaceText(0, text, cdataFlag)
+}
+
+// Tail returns all character data immediately following the element's end
+// tag.
+func (e *Element) Tail() string {
+ if e.Parent() == nil {
+ return ""
+ }
+
+ p := e.Parent()
+ i := e.Index()
+
+ text := ""
+ for _, ch := range p.Child[i+1:] {
+ if cd, ok := ch.(*CharData); ok {
+ if text == "" {
+ text = cd.Data
+ } else {
+ text += cd.Data
+ }
+ } else {
+ break
+ }
+ }
+ return text
+}
+
+// SetTail replaces all character data immediately following the element's end
+// tag with the requested string.
+func (e *Element) SetTail(text string) {
+ if e.Parent() == nil {
+ return
+ }
+
+ p := e.Parent()
+ p.replaceText(e.Index()+1, text, 0)
+}
+
+// replaceText is a helper function that replaces a series of chardata tokens
+// starting at index i with the requested text.
+func (e *Element) replaceText(i int, text string, flags charDataFlags) {
+ end := e.findTermCharDataIndex(i)
+
+ switch {
+ case end == i:
+ if text != "" {
+ // insert a new chardata token at index i
+ cd := newCharData(text, flags, nil)
+ e.InsertChildAt(i, cd)
+ }
+
+ case end == i+1:
+ if text == "" {
+ // remove the chardata token at index i
+ e.RemoveChildAt(i)
+ } else {
+ // replace the first and only character token at index i
+ cd := e.Child[i].(*CharData)
+ cd.Data, cd.flags = text, flags
+ }
+
+ default:
+ if text == "" {
+ // remove all chardata tokens starting from index i
+ copy(e.Child[i:], e.Child[end:])
+ removed := end - i
+ e.Child = e.Child[:len(e.Child)-removed]
+ for j := i; j < len(e.Child); j++ {
+ e.Child[j].setIndex(j)
+ }
+ } else {
+ // replace the first chardata token at index i and remove all
+ // subsequent chardata tokens
+ cd := e.Child[i].(*CharData)
+ cd.Data, cd.flags = text, flags
+ copy(e.Child[i+1:], e.Child[end:])
+ removed := end - (i + 1)
+ e.Child = e.Child[:len(e.Child)-removed]
+ for j := i + 1; j < len(e.Child); j++ {
+ e.Child[j].setIndex(j)
+ }
+ }
+ }
+}
+
+// findTermCharDataIndex finds the index of the first child token that isn't
+// a CharData token. It starts from the requested start index.
+func (e *Element) findTermCharDataIndex(start int) int {
+ for i := start; i < len(e.Child); i++ {
+ if _, ok := e.Child[i].(*CharData); !ok {
+ return i
+ }
+ }
+ return len(e.Child)
+}
+
+// CreateElement creates a new element with the specified tag (i.e., name) and
+// adds it as the last child of element 'e'. The tag may include a prefix
+// followed by a colon.
+func (e *Element) CreateElement(tag string) *Element {
+ space, stag := spaceDecompose(tag)
+ return newElement(space, stag, e)
+}
+
+// CreateChild performs the same task as CreateElement but calls a
+// continuation function after the child element is created, allowing
+// additional actions to be performed on the child element before returning.
+//
+// This method of element creation is particularly useful when building nested
+// XML documents from code. For example:
+//
+// org := doc.CreateChild("organization", func(e *Element) {
+// e.CreateComment("Mary")
+// e.CreateChild("person", func(e *Element) {
+// e.CreateAttr("name", "Mary")
+// e.CreateAttr("age", "30")
+// e.CreateAttr("hair", "brown")
+// })
+// })
+func (e *Element) CreateChild(tag string, cont func(e *Element)) *Element {
+ child := e.CreateElement(tag)
+ cont(child)
+ return child
+}
+
+// AddChild adds the token 't' as the last child of the element. If token 't'
+// was already the child of another element, it is first removed from its
+// parent element.
+func (e *Element) AddChild(t Token) {
+ if t.Parent() != nil {
+ t.Parent().RemoveChild(t)
+ }
+ e.addChild(t)
+}
+
+// InsertChild inserts the token 't' into this element's list of children just
+// before the element's existing child token 'ex'. If the existing element
+// 'ex' does not appear in this element's list of child tokens, then 't' is
+// added to the end of this element's list of child tokens. If token 't' is
+// already the child of another element, it is first removed from the other
+// element's list of child tokens.
+//
+// Deprecated: InsertChild is deprecated. Use InsertChildAt instead.
+func (e *Element) InsertChild(ex Token, t Token) {
+ if ex == nil || ex.Parent() != e {
+ e.AddChild(t)
+ return
+ }
+
+ if t.Parent() != nil {
+ t.Parent().RemoveChild(t)
+ }
+
+ t.setParent(e)
+
+ i := ex.Index()
+ e.Child = append(e.Child, nil)
+ copy(e.Child[i+1:], e.Child[i:])
+ e.Child[i] = t
+
+ for j := i; j < len(e.Child); j++ {
+ e.Child[j].setIndex(j)
+ }
+}
+
+// InsertChildAt inserts the token 't' into this element's list of child
+// tokens just before the requested 'index'. If the index is greater than or
+// equal to the length of the list of child tokens, then the token 't' is
+// added to the end of the list of child tokens.
+func (e *Element) InsertChildAt(index int, t Token) {
+ if index >= len(e.Child) {
+ e.AddChild(t)
+ return
+ }
+
+ if t.Parent() != nil {
+ if t.Parent() == e && t.Index() < index {
+ index--
+ }
+ t.Parent().RemoveChild(t)
+ }
+
+ t.setParent(e)
+
+ e.Child = append(e.Child, nil)
+ copy(e.Child[index+1:], e.Child[index:])
+ e.Child[index] = t
+
+ for j := index; j < len(e.Child); j++ {
+ e.Child[j].setIndex(j)
+ }
+}
+
+// RemoveChild attempts to remove the token 't' from this element's list of
+// child tokens. If the token 't' was a child of this element, then it is
+// removed and returned. Otherwise, nil is returned.
+func (e *Element) RemoveChild(t Token) Token {
+ if t.Parent() != e {
+ return nil
+ }
+ return e.RemoveChildAt(t.Index())
+}
+
+// RemoveChildAt removes the child token appearing in slot 'index' of this
+// element's list of child tokens. The removed child token is then returned.
+// If the index is out of bounds, no child is removed and nil is returned.
+func (e *Element) RemoveChildAt(index int) Token {
+ if index >= len(e.Child) {
+ return nil
+ }
+
+ t := e.Child[index]
+ for j := index + 1; j < len(e.Child); j++ {
+ e.Child[j].setIndex(j - 1)
+ }
+ e.Child = append(e.Child[:index], e.Child[index+1:]...)
+ t.setIndex(-1)
+ t.setParent(nil)
+ return t
+}
+
+// autoClose analyzes the stack's top element and the current token to decide
+// whether the top element should be closed.
+func (e *Element) autoClose(stack *stack[*Element], t xml.Token, tags []string) {
+ if stack.empty() {
+ return
+ }
+
+ top := stack.peek()
+
+ for _, tag := range tags {
+ if strings.EqualFold(tag, top.FullTag()) {
+ if e, ok := t.(xml.EndElement); !ok ||
+ !strings.EqualFold(e.Name.Space, top.Space) ||
+ !strings.EqualFold(e.Name.Local, top.Tag) {
+ stack.pop()
+ }
+ break
+ }
+ }
+}
+
+// ReadFrom reads XML from the reader 'ri' and stores the result as a new
+// child of this element.
+func (e *Element) readFrom(ri io.Reader, settings ReadSettings) (n int64, err error) {
+ var r xmlReader
+ var pr *xmlPeekReader
+ if settings.PreserveCData {
+ pr = newXmlPeekReader(ri)
+ r = pr
+ } else {
+ r = newXmlSimpleReader(ri)
+ }
+
+ attrCheck := make(map[xml.Name]int)
+ dec := newDecoder(r, settings)
+
+ var stack stack[*Element]
+ stack.push(e)
+ for {
+ if pr != nil {
+ pr.PeekPrepare(dec.InputOffset(), len(cdataPrefix))
+ }
+
+ t, err := dec.RawToken()
+
+ if settings.Permissive && settings.AutoClose != nil {
+ e.autoClose(&stack, t, settings.AutoClose)
+ }
+
+ switch {
+ case err == io.EOF:
+ if len(stack.data) != 1 {
+ return r.Bytes(), ErrXML
+ }
+ return r.Bytes(), nil
+ case err != nil:
+ return r.Bytes(), err
+ case stack.empty():
+ return r.Bytes(), ErrXML
+ }
+
+ top := stack.peek()
+
+ switch t := t.(type) {
+ case xml.StartElement:
+ e := newElement(t.Name.Space, t.Name.Local, top)
+ if settings.PreserveDuplicateAttrs || len(t.Attr) < 2 {
+ for _, a := range t.Attr {
+ e.addAttr(a.Name.Space, a.Name.Local, a.Value)
+ }
+ } else {
+ for _, a := range t.Attr {
+ if i, contains := attrCheck[a.Name]; contains {
+ e.Attr[i].Value = a.Value
+ } else {
+ attrCheck[a.Name] = e.addAttr(a.Name.Space, a.Name.Local, a.Value)
+ }
+ }
+ clear(attrCheck)
+ }
+ stack.push(e)
+ case xml.EndElement:
+ if top.Tag != t.Name.Local || top.Space != t.Name.Space {
+ return r.Bytes(), ErrXML
+ }
+ stack.pop()
+ case xml.CharData:
+ data := string(t)
+ var flags charDataFlags
+ if pr != nil {
+ peekBuf := pr.PeekFinalize()
+ if bytes.Equal(peekBuf, cdataPrefix) {
+ flags = cdataFlag
+ } else if isWhitespace(data) {
+ flags = whitespaceFlag
+ }
+ } else {
+ if isWhitespace(data) {
+ flags = whitespaceFlag
+ }
+ }
+ newCharData(data, flags, top)
+ case xml.Comment:
+ newComment(string(t), top)
+ case xml.Directive:
+ newDirective(string(t), top)
+ case xml.ProcInst:
+ newProcInst(t.Target, string(t.Inst), top)
+ }
+ }
+}
+
+// SelectAttr finds an element attribute matching the requested 'key' and, if
+// found, returns a pointer to the matching attribute. The function returns
+// nil if no matching attribute is found. The key may include a namespace
+// prefix followed by a colon.
+func (e *Element) SelectAttr(key string) *Attr {
+ space, skey := spaceDecompose(key)
+ for i, a := range e.Attr {
+ if spaceMatch(space, a.Space) && skey == a.Key {
+ return &e.Attr[i]
+ }
+ }
+ return nil
+}
+
+// SelectAttrValue finds an element attribute matching the requested 'key' and
+// returns its value if found. If no matching attribute is found, the function
+// returns the 'dflt' value instead. The key may include a namespace prefix
+// followed by a colon.
+func (e *Element) SelectAttrValue(key, dflt string) string {
+ space, skey := spaceDecompose(key)
+ for _, a := range e.Attr {
+ if spaceMatch(space, a.Space) && skey == a.Key {
+ return a.Value
+ }
+ }
+ return dflt
+}
+
+// ChildElements returns all elements that are children of this element.
+func (e *Element) ChildElements() []*Element {
+ return slices.Collect(e.ChildElementsSeq())
+}
+
+// ChildElementsSeq returns an iterator over all child elements of this
+// element.
+func (e *Element) ChildElementsSeq() iter.Seq[*Element] {
+ return func(yield func(*Element) bool) {
+ for _, t := range e.Child {
+ if c, ok := t.(*Element); ok {
+ if !yield(c) {
+ return
+ }
+ }
+ }
+ }
+}
+
+// SelectElement returns the first child element with the given 'tag' (i.e.,
+// name). The function returns nil if no child element matching the tag is
+// found. The tag may include a namespace prefix followed by a colon.
+func (e *Element) SelectElement(tag string) *Element {
+ for element := range e.SelectElementsSeq(tag) {
+ return element
+ }
+ return nil
+}
+
+// SelectElements returns a slice of all child elements with the given 'tag'
+// (i.e., name). The tag may include a namespace prefix followed by a colon.
+func (e *Element) SelectElements(tag string) []*Element {
+ return slices.Collect(e.SelectElementsSeq(tag))
+}
+
+// SelectElementsSeq returns an iterator over all child elements with the
+// given 'tag' (i.e., name). The tag may include a namespace prefix followed
+// by a colon.
+func (e *Element) SelectElementsSeq(tag string) iter.Seq[*Element] {
+ return func(yield func(*Element) bool) {
+ space, stag := spaceDecompose(tag)
+ for _, t := range e.Child {
+ if c, ok := t.(*Element); ok && spaceMatch(space, c.Space) && stag == c.Tag {
+ if !yield(c) {
+ return
+ }
+ }
+ }
+ }
+}
+
+// FindElement returns the first element matched by the XPath-like 'path'
+// string. The function returns nil if no child element is found using the
+// path. It panics if an invalid path string is supplied.
+func (e *Element) FindElement(path string) *Element {
+ return e.FindElementPath(MustCompilePath(path))
+}
+
+// FindElementPath returns the first element matched by the 'path' object. The
+// function returns nil if no element is found using the path.
+func (e *Element) FindElementPath(path Path) *Element {
+ for element := range path.traverse(e) {
+ return element
+ }
+ return nil
+}
+
+// FindElements returns a slice of elements matched by the XPath-like 'path'
+// string. The function returns nil if no child element is found using the
+// path. It panics if an invalid path string is supplied.
+func (e *Element) FindElements(path string) []*Element {
+ return slices.Collect(e.FindElementsSeq(path))
+}
+
+// FindElementsSeq returns an iterator over elements matched by the XPath-like
+// 'path' string. This function uses Go's iterator support for
+// memory-efficient traversal. It panics if an invalid path string is
+// supplied.
+func (e *Element) FindElementsSeq(path string) iter.Seq[*Element] {
+ return e.FindElementsPathSeq(MustCompilePath(path))
+}
+
+// FindElementsPath returns a slice of elements matched by the 'path' object.
+func (e *Element) FindElementsPath(path Path) []*Element {
+ return slices.Collect(e.FindElementsPathSeq(path))
+}
+
+// FindElementsPathSeq returns an iterator over elements matched by the 'path'
+// object.
+func (e *Element) FindElementsPathSeq(path Path) iter.Seq[*Element] {
+ return path.traverse(e)
+}
+
+// NotNil returns the receiver element if it isn't nil; otherwise, it returns
+// an unparented element with an empty string tag. This function simplifies
+// the task of writing code to ignore not-found results from element queries.
+// For example, instead of writing this:
+//
+// if e := doc.SelectElement("enabled"); e != nil {
+// e.SetText("true")
+// }
+//
+// You could write this:
+//
+// doc.SelectElement("enabled").NotNil().SetText("true")
+func (e *Element) NotNil() *Element {
+ if e == nil {
+ return NewElement("")
+ }
+ return e
+}
+
+// GetPath returns the absolute path of the element. The absolute path is the
+// full path from the document's root.
+func (e *Element) GetPath() string {
+ path := []string{}
+ for seg := e; seg != nil; seg = seg.Parent() {
+ if seg.Tag != "" {
+ path = append(path, seg.Tag)
+ }
+ }
+
+ // Reverse the path.
+ for i, j := 0, len(path)-1; i < j; i, j = i+1, j-1 {
+ path[i], path[j] = path[j], path[i]
+ }
+
+ return "/" + strings.Join(path, "/")
+}
+
+// GetRelativePath returns the path of this element relative to the 'source'
+// element. If the two elements are not part of the same element tree, then
+// the function returns the empty string.
+func (e *Element) GetRelativePath(source *Element) string {
+ var path []*Element
+
+ if source == nil {
+ return ""
+ }
+
+ // Build a reverse path from the element toward the root. Stop if the
+ // source element is encountered.
+ var seg *Element
+ for seg = e; seg != nil && seg != source; seg = seg.Parent() {
+ path = append(path, seg)
+ }
+
+ // If we found the source element, reverse the path and compose the
+ // string.
+ if seg == source {
+ if len(path) == 0 {
+ return "."
+ }
+ parts := []string{}
+ for i := len(path) - 1; i >= 0; i-- {
+ parts = append(parts, path[i].Tag)
+ }
+ return "./" + strings.Join(parts, "/")
+ }
+
+ // The source wasn't encountered, so climb from the source element toward
+ // the root of the tree until an element in the reversed path is
+ // encountered.
+
+ findPathIndex := func(e *Element, path []*Element) int {
+ for i, ee := range path {
+ if e == ee {
+ return i
+ }
+ }
+ return -1
+ }
+
+ climb := 0
+ for seg = source; seg != nil; seg = seg.Parent() {
+ i := findPathIndex(seg, path)
+ if i >= 0 {
+ path = path[:i] // truncate at found segment
+ break
+ }
+ climb++
+ }
+
+ // No element in the reversed path was encountered, so the two elements
+ // must not be part of the same tree.
+ if seg == nil {
+ return ""
+ }
+
+ // Reverse the (possibly truncated) path and prepend ".." segments to
+ // climb.
+ parts := []string{}
+ for i := 0; i < climb; i++ {
+ parts = append(parts, "..")
+ }
+ for i := len(path) - 1; i >= 0; i-- {
+ parts = append(parts, path[i].Tag)
+ }
+ return strings.Join(parts, "/")
+}
+
+// IndentWithSettings modifies the element and its child tree by inserting
+// character data tokens containing newlines and indentation. The behavior of
+// the indentation algorithm is configured by the indent settings. Because
+// this function indents the element as if it were at the root of a document,
+// it is most useful when called just before writing the element as an XML
+// fragment using WriteTo.
+func (e *Element) IndentWithSettings(s *IndentSettings) {
+ e.indent(1, getIndentFunc(s), s)
+}
+
+// indent recursively inserts proper indentation between an XML element's
+// child tokens.
+func (e *Element) indent(depth int, indent indentFunc, s *IndentSettings) {
+ e.stripIndent(s)
+ n := len(e.Child)
+ if n == 0 {
+ return
+ }
+
+ oldChild := e.Child
+ e.Child = make([]Token, 0, n*2+1)
+ isCharData, firstNonCharData := false, true
+ for _, c := range oldChild {
+ // Insert NL+indent before child if it's not character data.
+ // Exceptions: when it's the first non-character-data child, or when
+ // the child is at root depth.
+ _, isCharData = c.(*CharData)
+ if !isCharData {
+ if !firstNonCharData || depth > 0 {
+ s := indent(depth)
+ if s != "" {
+ newCharData(s, whitespaceFlag, e)
+ }
+ }
+ firstNonCharData = false
+ }
+
+ e.addChild(c)
+
+ // Recursively process child elements.
+ if ce, ok := c.(*Element); ok {
+ ce.indent(depth+1, indent, s)
+ }
+ }
+
+ // Insert NL+indent before the last child.
+ if !isCharData {
+ if !firstNonCharData || depth > 0 {
+ s := indent(depth - 1)
+ if s != "" {
+ newCharData(s, whitespaceFlag, e)
+ }
+ }
+ }
+}
+
+// stripIndent removes any previously inserted indentation.
+func (e *Element) stripIndent(s *IndentSettings) {
+ // Count the number of non-indent child tokens
+ n := len(e.Child)
+ for _, c := range e.Child {
+ if cd, ok := c.(*CharData); ok && cd.IsWhitespace() {
+ n--
+ }
+ }
+ if n == len(e.Child) {
+ return
+ }
+ if n == 0 && len(e.Child) == 1 && s.PreserveLeafWhitespace {
+ return
+ }
+
+ // Strip out indent CharData
+ newChild := make([]Token, n)
+ j := 0
+ for _, c := range e.Child {
+ if cd, ok := c.(*CharData); ok && cd.IsWhitespace() {
+ continue
+ }
+ newChild[j] = c
+ newChild[j].setIndex(j)
+ j++
+ }
+ e.Child = newChild
+}
+
+// stripTrailingWhitespace removes any trailing whitespace CharData tokens
+// from the element's children.
+func (e *Element) stripTrailingWhitespace() {
+ for i := len(e.Child) - 1; i >= 0; i-- {
+ if cd, ok := e.Child[i].(*CharData); !ok || !cd.IsWhitespace() {
+ e.Child = e.Child[:i+1]
+ return
+ }
+ }
+}
+
+// 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,
+ index: e.index,
+ }
+ for i, t := range e.Child {
+ ne.Child[i] = t.dup(ne)
+ }
+ copy(ne.Attr, e.Attr)
+ return ne
+}
+
+// NextSibling returns this element's next sibling element. It returns nil if
+// there is no next sibling element.
+func (e *Element) NextSibling() *Element {
+ if e.parent == nil {
+ return nil
+ }
+ for i := e.index + 1; i < len(e.parent.Child); i++ {
+ if s, ok := e.parent.Child[i].(*Element); ok {
+ return s
+ }
+ }
+ return nil
+}
+
+// PrevSibling returns this element's preceding sibling element. It returns
+// nil if there is no preceding sibling element.
+func (e *Element) PrevSibling() *Element {
+ if e.parent == nil {
+ return nil
+ }
+ for i := e.index - 1; i >= 0; i-- {
+ if s, ok := e.parent.Child[i].(*Element); ok {
+ return s
+ }
+ }
+ return nil
+}
+
+// Parent returns this element's parent element. It returns nil if this
+// element has no parent.
+func (e *Element) Parent() *Element {
+ return e.parent
+}
+
+// Index returns the index of this element within its parent element's
+// list of child tokens. If this element has no parent, then the function
+// returns -1.
+func (e *Element) Index() int {
+ return e.index
+}
+
+// WriteTo serializes the element to the writer w.
+func (e *Element) WriteTo(w Writer, s *WriteSettings) {
+ w.WriteByte('<')
+ w.WriteString(e.FullTag())
+ for _, a := range e.Attr {
+ w.WriteByte(' ')
+ a.WriteTo(w, s)
+ }
+ if len(e.Child) > 0 {
+ w.WriteByte('>')
+ for _, c := range e.Child {
+ c.WriteTo(w, s)
+ }
+ w.Write([]byte{'<', '/'})
+ w.WriteString(e.FullTag())
+ w.WriteByte('>')
+ } else {
+ if s.CanonicalEndTags {
+ w.Write([]byte{'>', '<', '/'})
+ w.WriteString(e.FullTag())
+ w.WriteByte('>')
+ } else {
+ w.Write([]byte{'/', '>'})
+ }
+ }
+}
+
+// setParent replaces this element token's parent.
+func (e *Element) setParent(parent *Element) {
+ e.parent = parent
+}
+
+// setIndex sets this element token's index within its parent's Child slice.
+func (e *Element) setIndex(index int) {
+ e.index = index
+}
+
+// addChild adds a child token to the element e.
+func (e *Element) addChild(t Token) {
+ t.setParent(e)
+ t.setIndex(len(e.Child))
+ e.Child = append(e.Child, t)
+}
+
+// CreateAttr creates an attribute with the specified 'key' and 'value' and
+// adds it to this element. If an attribute with same key already exists on
+// this element, then its value is replaced. The key may include a namespace
+// prefix followed by a colon.
+func (e *Element) CreateAttr(key, value string) *Attr {
+ space, skey := spaceDecompose(key)
+
+ for i, a := range e.Attr {
+ if space == a.Space && skey == a.Key {
+ e.Attr[i].Value = value
+ return &e.Attr[i]
+ }
+ }
+
+ i := e.addAttr(space, skey, value)
+ return &e.Attr[i]
+}
+
+// addAttr is a helper function that adds an attribute to an element. Returns
+// the index of the added attribute.
+func (e *Element) addAttr(space, key, value string) int {
+ a := Attr{
+ Space: space,
+ Key: key,
+ Value: value,
+ element: e,
+ }
+ e.Attr = append(e.Attr, a)
+ return len(e.Attr) - 1
+}
+
+// RemoveAttr removes the first attribute of this element whose key matches
+// 'key'. It returns a copy of the removed attribute if a match is found. If
+// no match is found, it returns nil. The key may include a namespace prefix
+// followed by a colon.
+func (e *Element) RemoveAttr(key string) *Attr {
+ space, skey := spaceDecompose(key)
+ for i, a := range e.Attr {
+ if space == a.Space && skey == a.Key {
+ e.Attr = append(e.Attr[0:i], e.Attr[i+1:]...)
+ return &Attr{
+ Space: a.Space,
+ Key: a.Key,
+ Value: a.Value,
+ element: nil,
+ }
+ }
+ }
+ return nil
+}
+
+// SortAttrs sorts this element's attributes lexicographically by key.
+func (e *Element) SortAttrs() {
+ slices.SortFunc(e.Attr, func(a, b Attr) int {
+ if v := strings.Compare(a.Space, b.Space); v != 0 {
+ return v
+ }
+ return strings.Compare(a.Key, b.Key)
+ })
+}
+
+// FullKey returns this attribute's complete key, including namespace prefix
+// if present.
+func (a *Attr) FullKey() string {
+ if a.Space == "" {
+ return a.Key
+ }
+ return a.Space + ":" + a.Key
+}
+
+// Element returns a pointer to the element containing this attribute.
+func (a *Attr) Element() *Element {
+ return a.element
+}
+
+// NamespaceURI returns the XML namespace URI associated with this attribute.
+// The function returns the empty string if the attribute is unprefixed or
+// if the attribute is part of the XML default namespace.
+func (a *Attr) NamespaceURI() string {
+ if a.Space == "" {
+ return ""
+ }
+ return a.element.findLocalNamespaceURI(a.Space)
+}
+
+// WriteTo serializes the attribute to the writer.
+func (a *Attr) WriteTo(w Writer, s *WriteSettings) {
+ w.WriteString(a.FullKey())
+ if s.AttrSingleQuote {
+ w.WriteString(`='`)
+ } else {
+ w.WriteString(`="`)
+ }
+ var m escapeMode
+ if s.CanonicalAttrVal && !s.AttrSingleQuote {
+ m = escapeCanonicalAttr
+ } else {
+ m = escapeNormal
+ }
+ escapeString(w, a.Value, m)
+ if s.AttrSingleQuote {
+ w.WriteByte('\'')
+ } else {
+ w.WriteByte('"')
+ }
+}
+
+// NewText creates an unparented CharData token containing simple text data.
+func NewText(text string) *CharData {
+ return newCharData(text, 0, nil)
+}
+
+// NewCData creates an unparented XML character CDATA section with 'data' as
+// its content.
+func NewCData(data string) *CharData {
+ return newCharData(data, cdataFlag, nil)
+}
+
+// NewCharData creates an unparented CharData token containing simple text
+// data.
+//
+// Deprecated: NewCharData is deprecated. Instead, use NewText, which does the
+// same thing.
+func NewCharData(data string) *CharData {
+ return newCharData(data, 0, nil)
+}
+
+// newCharData creates a character data token and binds it to a parent
+// element. If parent is nil, the CharData token remains unbound.
+func newCharData(data string, flags charDataFlags, parent *Element) *CharData {
+ c := &CharData{
+ Data: data,
+ parent: nil,
+ index: -1,
+ flags: flags,
+ }
+ if parent != nil {
+ parent.addChild(c)
+ }
+ return c
+}
+
+// CreateText creates a CharData token containing simple text data and adds it
+// to the end of this element's list of child tokens.
+func (e *Element) CreateText(text string) *CharData {
+ return newCharData(text, 0, e)
+}
+
+// CreateCData creates a CharData token containing a CDATA section with 'data'
+// as its content and adds it to the end of this element's list of child
+// tokens.
+func (e *Element) CreateCData(data string) *CharData {
+ return newCharData(data, cdataFlag, e)
+}
+
+// CreateCharData creates a CharData token containing simple text data and
+// adds it to the end of this element's list of child tokens.
+//
+// Deprecated: CreateCharData is deprecated. Instead, use CreateText, which
+// does the same thing.
+func (e *Element) CreateCharData(data string) *CharData {
+ return e.CreateText(data)
+}
+
+// SetData modifies the content of the CharData token. In the case of a
+// CharData token containing simple text, the simple text is modified. In the
+// case of a CharData token containing a CDATA section, the CDATA section's
+// content is modified.
+func (c *CharData) SetData(text string) {
+ c.Data = text
+ if isWhitespace(text) {
+ c.flags |= whitespaceFlag
+ } else {
+ c.flags &= ^whitespaceFlag
+ }
+}
+
+// IsCData returns true if this CharData token is contains a CDATA section. It
+// returns false if the CharData token contains simple text.
+func (c *CharData) IsCData() bool {
+ return (c.flags & cdataFlag) != 0
+}
+
+// IsWhitespace returns true if this CharData token contains only whitespace.
+func (c *CharData) IsWhitespace() bool {
+ return (c.flags & whitespaceFlag) != 0
+}
+
+// Parent returns this CharData token's parent element, or nil if it has no
+// parent.
+func (c *CharData) Parent() *Element {
+ return c.parent
+}
+
+// Index returns the index of this CharData token within its parent element's
+// list of child tokens. If this CharData token has no parent, then the
+// function returns -1.
+func (c *CharData) Index() int {
+ return c.index
+}
+
+// WriteTo serializes character data to the writer.
+func (c *CharData) WriteTo(w Writer, s *WriteSettings) {
+ if c.IsCData() {
+ w.WriteString(`<![CDATA[`)
+ w.WriteString(c.Data)
+ w.WriteString(`]]>`)
+ } else {
+ var m escapeMode
+ if s.CanonicalText {
+ m = escapeCanonicalText
+ } else {
+ m = escapeNormal
+ }
+ escapeString(w, c.Data, m)
+ }
+}
+
+// dup duplicates the character data.
+func (c *CharData) dup(parent *Element) Token {
+ return &CharData{
+ Data: c.Data,
+ flags: c.flags,
+ parent: parent,
+ index: c.index,
+ }
+}
+
+// setParent replaces the character data token's parent.
+func (c *CharData) setParent(parent *Element) {
+ c.parent = parent
+}
+
+// setIndex sets the CharData token's index within its parent element's Child
+// slice.
+func (c *CharData) setIndex(index int) {
+ c.index = index
+}
+
+// NewComment creates an unparented comment token.
+func NewComment(comment string) *Comment {
+ return newComment(comment, nil)
+}
+
+// NewComment creates a comment token and sets its parent element to 'parent'.
+func newComment(comment string, parent *Element) *Comment {
+ c := &Comment{
+ Data: comment,
+ parent: nil,
+ index: -1,
+ }
+ if parent != nil {
+ parent.addChild(c)
+ }
+ return c
+}
+
+// CreateComment creates a comment token using the specified 'comment' string
+// and adds it as the last child token of this element.
+func (e *Element) CreateComment(comment string) *Comment {
+ return newComment(comment, e)
+}
+
+// dup duplicates the comment.
+func (c *Comment) dup(parent *Element) Token {
+ return &Comment{
+ Data: c.Data,
+ parent: parent,
+ index: c.index,
+ }
+}
+
+// Parent returns comment token's parent element, or nil if it has no parent.
+func (c *Comment) Parent() *Element {
+ return c.parent
+}
+
+// Index returns the index of this Comment token within its parent element's
+// list of child tokens. If this Comment token has no parent, then the
+// function returns -1.
+func (c *Comment) Index() int {
+ return c.index
+}
+
+// WriteTo serialies the comment to the writer.
+func (c *Comment) WriteTo(w Writer, s *WriteSettings) {
+ w.WriteString("<!--")
+ w.WriteString(c.Data)
+ w.WriteString("-->")
+}
+
+// setParent replaces the comment token's parent.
+func (c *Comment) setParent(parent *Element) {
+ c.parent = parent
+}
+
+// setIndex sets the Comment token's index within its parent element's Child
+// slice.
+func (c *Comment) setIndex(index int) {
+ c.index = index
+}
+
+// NewDirective creates an unparented XML directive token.
+func NewDirective(data string) *Directive {
+ return newDirective(data, nil)
+}
+
+// newDirective creates an XML directive and binds it to a parent element. If
+// parent is nil, the Directive remains unbound.
+func newDirective(data string, parent *Element) *Directive {
+ d := &Directive{
+ Data: data,
+ parent: nil,
+ index: -1,
+ }
+ if parent != nil {
+ parent.addChild(d)
+ }
+ return d
+}
+
+// CreateDirective creates an XML directive token with the specified 'data'
+// value and adds it as the last child token of this element.
+func (e *Element) CreateDirective(data string) *Directive {
+ return newDirective(data, e)
+}
+
+// dup duplicates the directive.
+func (d *Directive) dup(parent *Element) Token {
+ return &Directive{
+ Data: d.Data,
+ parent: parent,
+ index: d.index,
+ }
+}
+
+// Parent returns directive token's parent element, or nil if it has no
+// parent.
+func (d *Directive) Parent() *Element {
+ return d.parent
+}
+
+// Index returns the index of this Directive token within its parent element's
+// list of child tokens. If this Directive token has no parent, then the
+// function returns -1.
+func (d *Directive) Index() int {
+ return d.index
+}
+
+// WriteTo serializes the XML directive to the writer.
+func (d *Directive) WriteTo(w Writer, s *WriteSettings) {
+ w.WriteString("<!")
+ w.WriteString(d.Data)
+ w.WriteString(">")
+}
+
+// setParent replaces the directive token's parent.
+func (d *Directive) setParent(parent *Element) {
+ d.parent = parent
+}
+
+// setIndex sets the Directive token's index within its parent element's Child
+// slice.
+func (d *Directive) setIndex(index int) {
+ d.index = index
+}
+
+// NewProcInst creates an unparented XML processing instruction.
+func NewProcInst(target, inst string) *ProcInst {
+ return newProcInst(target, inst, nil)
+}
+
+// newProcInst creates an XML processing instruction and binds it to a parent
+// element. If parent is nil, the ProcInst remains unbound.
+func newProcInst(target, inst string, parent *Element) *ProcInst {
+ p := &ProcInst{
+ Target: target,
+ Inst: inst,
+ parent: nil,
+ index: -1,
+ }
+ if parent != nil {
+ parent.addChild(p)
+ }
+ return p
+}
+
+// CreateProcInst creates an XML processing instruction token with the
+// specified 'target' and instruction 'inst'. It is then added as the last
+// child token of this element.
+func (e *Element) CreateProcInst(target, inst string) *ProcInst {
+ return newProcInst(target, inst, e)
+}
+
+// dup duplicates the procinst.
+func (p *ProcInst) dup(parent *Element) Token {
+ return &ProcInst{
+ Target: p.Target,
+ Inst: p.Inst,
+ parent: parent,
+ index: p.index,
+ }
+}
+
+// Parent returns processing instruction token's parent element, or nil if it
+// has no parent.
+func (p *ProcInst) Parent() *Element {
+ return p.parent
+}
+
+// Index returns the index of this ProcInst token within its parent element's
+// list of child tokens. If this ProcInst token has no parent, then the
+// function returns -1.
+func (p *ProcInst) Index() int {
+ return p.index
+}
+
+// WriteTo serializes the processing instruction to the writer.
+func (p *ProcInst) WriteTo(w Writer, s *WriteSettings) {
+ w.WriteString("<?")
+ w.WriteString(p.Target)
+ if p.Inst != "" {
+ w.WriteByte(' ')
+ w.WriteString(p.Inst)
+ }
+ w.WriteString("?>")
+}
+
+// setParent replaces the processing instruction token's parent.
+func (p *ProcInst) setParent(parent *Element) {
+ p.parent = parent
+}
+
+// setIndex sets the processing instruction token's index within its parent
+// element's Child slice.
+func (p *ProcInst) setIndex(index int) {
+ p.index = index
+}