Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package rawxml
- import (
- "bytes"
- "encoding/xml"
- "errors"
- "fmt"
- "sort"
- "strings"
- )
- var errTODO = errors.New("TODO")
- type errUnexpectedToken struct {
- Token xml.Token
- }
- func (s errUnexpectedToken) Error() string {
- return fmt.Sprintf("unexpected token: %#v", s.Token)
- }
- type errRedefinedPrefix struct {
- Prefix string
- }
- func (s errRedefinedPrefix) Error() string {
- return fmt.Sprintf("redefined prefix: %v", s.Prefix)
- }
- type xmlns struct {
- Parent *xmlns
- Spaces map[string]string
- }
- func (s *xmlns) register(prefix, space string) (err error) {
- if defined, ok := s.Spaces[prefix]; ok {
- if defined != space {
- err = &errRedefinedPrefix{Prefix: prefix}
- }
- } else {
- s.Spaces[prefix] = space
- }
- return
- }
- func (s *xmlns) encodeName(v Name) (r xml.Name, err error) {
- var prefix string
- if pl := strings.SplitN(v.Local, ":", 2); len(pl) == 2 {
- prefix, r.Local = pl[0], pl[1]
- }
- r.Local = v.Local
- var known bool
- for ns := s; ns != nil && !known; ns = ns.Parent {
- known = (ns.Spaces[prefix] == v.Space)
- }
- if !known {
- err = s.register(prefix, v.Space)
- }
- return
- }
- func (s *xmlns) decodeName(v xml.Name) (r Name, err error) {
- for ns := s; ns != nil && err == nil && r.Space == ""; ns = ns.Parent {
- if v, ok := ns.Spaces[v.Space]; ok {
- r.Space = v
- }
- }
- if v.Space != "" {
- r.Local = strings.Join([]string{v.Space, v.Local}, ":")
- } else {
- r.Local = v.Local
- }
- return
- }
- func (s *xmlns) encodeAttrs(v Attrs) (r []xml.Attr, err error) {
- for i := 0; i < len(v) && err == nil; i++ {
- attr := xml.Attr{
- Value: v[i].Value,
- }
- if attr.Name, err = s.encodeName(v[i].Name); err == nil {
- r = append(r, attr)
- }
- }
- var prefixes []string
- for prefix := range s.Spaces {
- prefixes = append(prefixes, prefix)
- }
- sort.Strings(prefixes)
- for _, prefix := range prefixes {
- local := "xmlns"
- if prefix != "" {
- local = strings.Join([]string{local, prefix}, ":")
- }
- attr := xml.Attr{
- Name: xml.Name{Local: local},
- Value: s.Spaces[prefix],
- }
- r = append(r, attr)
- }
- return
- }
- func (s *xmlns) decodeAttrs(v []xml.Attr) (r Attrs, err error) {
- var left []int
- for i := 0; i < len(v) && err == nil; i++ {
- space, local, value := v[i].Name.Space, v[i].Name.Local, v[i].Value
- if space == "" && local == "xmlns" {
- err = s.register(space, value)
- } else if space == "xmlns" {
- err = s.register(local, value)
- } else {
- left = append(left, i)
- }
- }
- for j := 0; j < len(left) && err == nil; j++ {
- i := left[j]
- attr := Attr{
- Value: v[i].Value,
- }
- if attr.Name, err = s.decodeName(v[i].Name); err == nil {
- r = append(r, attr)
- }
- }
- return
- }
- // Name -
- type Name struct {
- Space string
- Local string
- }
- // Attr -
- type Attr struct {
- Name Name
- Value string
- }
- // Attrs -
- type Attrs []Attr
- // Content -
- type Content [][]byte
- func (s Content) isWhiteSpaces() bool {
- ok := true
- for i := 0; i < len(s) && ok; i++ {
- ok = isWhiteSpaces(s[i])
- }
- return ok
- }
- // Element -
- type Element struct {
- Name Name
- Attrs Attrs
- Elements Elements
- Content Content
- }
- func (s Element) encode(encoder Encoder, parent *xmlns) (err error) {
- xmlns := &xmlns{
- Parent: parent,
- Spaces: map[string]string{},
- }
- var start xml.StartElement
- if start.Name, err = xmlns.encodeName(s.Name); err != nil {
- return
- }
- if start.Attr, err = xmlns.encodeAttrs(s.Attrs); err != nil {
- return
- }
- if err = encoder.EncodeToken(start); err != nil {
- return
- }
- emptyContent := s.Content.isWhiteSpaces()
- emptyElements := s.Elements.isEmpty()
- if !emptyElements {
- if !emptyContent {
- err = errTODO
- }
- for i := 0; i < len(s.Elements) && err == nil; i++ {
- err = s.Elements[i].encode(encoder, xmlns)
- }
- } else {
- if !emptyContent {
- v := bytes.Join([][]byte(s.Content), nil)
- err = encoder.EncodeToken(xml.CharData(v))
- }
- }
- err = encoder.EncodeToken(start.End())
- return
- }
- func (s *Element) decode(decoder Decoder, start *xml.StartElement, parent *xmlns) (err error) {
- xmlns := &xmlns{
- Parent: parent,
- Spaces: map[string]string{},
- }
- if s.Attrs, err = xmlns.decodeAttrs(start.Attr); err != nil {
- return
- }
- if s.Name, err = xmlns.decodeName(start.Name); err != nil {
- return
- }
- var end *xml.EndElement
- for end == nil && err == nil {
- var token xml.Token
- if token, err = decoder.RawToken(); err == nil {
- switch token.(type) {
- case xml.StartElement:
- c := xml.CopyToken(token)
- v := c.(xml.StartElement)
- var child Element
- if err = child.decode(decoder, &v, xmlns); err == nil {
- s.Elements = append(s.Elements, child)
- }
- case xml.EndElement:
- c := xml.CopyToken(token)
- v := c.(xml.EndElement)
- end = &v
- if end.Name != start.Name {
- err = errTODO
- }
- case xml.CharData:
- c := xml.CopyToken(token)
- v := c.(xml.CharData)
- s.Content = append(s.Content, v)
- }
- }
- }
- if err == nil && !s.Elements.isEmpty() {
- if s.Content.isWhiteSpaces() {
- s.Content = nil
- } else {
- err = errTODO
- }
- }
- return
- }
- // Elements -
- type Elements []Element
- func (s Elements) isEmpty() bool {
- return len(s) == 0
- }
- var isWhiteSpace = map[byte]bool{
- 0x20: true,
- 0x09: true,
- 0x0d: true,
- 0x0a: true,
- }
- func isWhiteSpaces(v []byte) bool {
- ok := true
- for i := 0; i < len(v) && ok; i++ {
- ok = isWhiteSpace[v[i]]
- }
- return ok
- }
- // Decoder -
- type Decoder interface {
- RawToken() (xml.Token, error)
- InputOffset() int64
- }
- // Decode -
- func Decode(decoder Decoder) (element *Element, err error) {
- begin := true
- for element == nil && err == nil {
- var token xml.Token
- if token, err = decoder.RawToken(); err == nil {
- var unexpected bool
- switch token.(type) {
- case xml.StartElement:
- c := xml.CopyToken(token)
- v := c.(xml.StartElement)
- var swap Element
- if err = swap.decode(decoder, &v, nil); err == nil {
- element = &swap
- }
- case xml.EndElement:
- unexpected = true
- case xml.CharData:
- unexpected = begin || !isWhiteSpaces(token.(xml.CharData))
- case xml.ProcInst:
- unexpected = !(begin && token.(xml.ProcInst).Target == "xml")
- default:
- unexpected = begin
- }
- begin = false
- if unexpected {
- err = &errUnexpectedToken{
- Token: xml.CopyToken(token),
- }
- }
- }
- }
- return
- }
- // Encoder -
- type Encoder interface {
- EncodeToken(xml.Token) error
- Flush() error
- }
- // Encode -
- func Encode(encoder Encoder, element *Element) (err error) {
- if err = element.encode(encoder, nil); err == nil {
- err = encoder.Flush()
- }
- return
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement