// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package html import ( "bufio" "fmt" "io" "os" ) type writer interface { io.Writer WriteByte(byte) os.Error WriteString(string) (int, os.Error) } // Render renders the parse tree n to the given writer. // // For 'well-formed' parse trees, calling Parse on the output of Render will // result in a clone of the original tree. // // 'Well-formed' is not formally specified, but calling Parse on arbitrary // input results in a 'well-formed' parse tree if Parse does not return an // error. Programmatically constructed trees are typically also 'well-formed', // but it is possible to construct a tree that, when rendered and re-parsed, // results in a different tree. A simple example is that a solitary text node // would become a tree containing , and elements. Another // example is that the programmatic equivalent of "abc" becomes // "abc". func Render(w io.Writer, n *Node) os.Error { if x, ok := w.(writer); ok { return render(x, n) } buf := bufio.NewWriter(w) if err := render(buf, n); err != nil { return err } return buf.Flush() } func render(w writer, n *Node) os.Error { // Render non-element nodes; these are the easy cases. switch n.Type { case ErrorNode: return os.NewError("html: cannot render an ErrorNode node") case TextNode: return escape(w, n.Data) case DocumentNode: for _, c := range n.Child { if err := render(w, c); err != nil { return err } } return nil case ElementNode: // No-op. case CommentNode: if _, err := w.WriteString(""); err != nil { return err } return nil case DoctypeNode: if _, err := w.WriteString("') default: return os.NewError("html: unknown node type") } // Render the opening tag. if err := w.WriteByte('<'); err != nil { return err } if _, err := w.WriteString(n.Data); err != nil { return err } for _, a := range n.Attr { if err := w.WriteByte(' '); err != nil { return err } if _, err := w.WriteString(a.Key); err != nil { return err } if _, err := w.WriteString(`="`); err != nil { return err } if err := escape(w, a.Val); err != nil { return err } if err := w.WriteByte('"'); err != nil { return err } } if voidElements[n.Data] { if len(n.Child) != 0 { return fmt.Errorf("html: void element <%s> has child nodes", n.Data) } _, err := w.WriteString("/>") return err } if err := w.WriteByte('>'); err != nil { return err } // Render any child nodes. switch n.Data { case "noembed", "noframes", "noscript", "script", "style": for _, c := range n.Child { if c.Type != TextNode { return fmt.Errorf("html: raw text element <%s> has non-text child node", n.Data) } if _, err := w.WriteString(c.Data); err != nil { return err } } case "textarea", "title": for _, c := range n.Child { if c.Type != TextNode { return fmt.Errorf("html: RCDATA element <%s> has non-text child node", n.Data) } if err := render(w, c); err != nil { return err } } default: for _, c := range n.Child { if err := render(w, c); err != nil { return err } } } // Render the closing tag. if _, err := w.WriteString("') } // Section 13.1.2, "Elements", gives this list of void elements. Void elements // are those that can't have any contents. var voidElements = map[string]bool{ "area": true, "base": true, "br": true, "col": true, "command": true, "embed": true, "hr": true, "img": true, "input": true, "keygen": true, "link": true, "meta": true, "param": true, "source": true, "track": true, "wbr": true, }