1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
|
// Tideland Go Library - Simple Markup Language - Builder
//
// Copyright (C) 2009-2017 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.
package sml
//--------------------
// IMPORTS
//--------------------
import (
"strings"
"github.com/tideland/golib/collections"
"github.com/tideland/golib/errors"
)
//--------------------
// NODE BUILDER
//--------------------
// NodeBuilder creates a node structure.
type NodeBuilder struct {
stack []*tagNode
done bool
}
// NewNodeBuilder return a new nnode builder.
func NewNodeBuilder() *NodeBuilder {
return &NodeBuilder{[]*tagNode{}, false}
}
// Root returns the root node of the read document.
func (nb *NodeBuilder) Root() (Node, error) {
if !nb.done {
return nil, errors.New(ErrBuilder, errorMessages, "building is not yet done")
}
return nb.stack[0], nil
}
// BeginTagNode implements the Builder interface.
func (nb *NodeBuilder) BeginTagNode(tag string) error {
if nb.done {
return errors.New(ErrBuilder, errorMessages, "building is already done")
}
t, err := newTagNode(tag)
if err != nil {
return err
}
nb.stack = append(nb.stack, t)
return nil
}
// EndTagNode implements the Builder interface.
func (nb *NodeBuilder) EndTagNode() error {
if nb.done {
return errors.New(ErrBuilder, errorMessages, "building is already done")
}
switch l := len(nb.stack); l {
case 0:
return errors.New(ErrBuilder, errorMessages, "no opening tag")
case 1:
nb.done = true
default:
nb.stack[l-2].appendChild(nb.stack[l-1])
nb.stack = nb.stack[:l-1]
}
return nil
}
// TextNode implements the Builder interface.
func (nb *NodeBuilder) TextNode(text string) error {
if nb.done {
return errors.New(ErrBuilder, errorMessages, "building is already done")
}
if len(nb.stack) > 0 {
nb.stack[len(nb.stack)-1].appendTextNode(text)
return nil
}
return errors.New(ErrBuilder, errorMessages, "no opening tag for text")
}
// RawNode implements the Builder interface.
func (nb *NodeBuilder) RawNode(raw string) error {
if nb.done {
return errors.New(ErrBuilder, errorMessages, "building is already done")
}
if len(nb.stack) > 0 {
nb.stack[len(nb.stack)-1].appendRawNode(raw)
return nil
}
return errors.New(ErrBuilder, errorMessages, "no opening tag for raw text")
}
// CommentNode implements the Builder interface.
func (nb *NodeBuilder) CommentNode(comment string) error {
if nb.done {
return errors.New(ErrBuilder, errorMessages, "building is already done")
}
if len(nb.stack) > 0 {
nb.stack[len(nb.stack)-1].appendCommentNode(comment)
return nil
}
return errors.New(ErrBuilder, errorMessages, "no opening tag for comment")
}
//--------------------
// KEY/STRING VALUE TREE BUILDER
//--------------------
// KeyStringValueTreeBuilder implements Builder to parse a
// file and create a KeyStringValueTree.
type KeyStringValueTreeBuilder struct {
stack collections.StringStack
tree collections.KeyStringValueTree
done bool
}
// NewKeyStringValueTreeBuilder return a new nnode builder.
func NewKeyStringValueTreeBuilder() *KeyStringValueTreeBuilder {
return &KeyStringValueTreeBuilder{}
}
// Tree returns the created tree.
func (tb *KeyStringValueTreeBuilder) Tree() (collections.KeyStringValueTree, error) {
if !tb.done {
return nil, errors.New(ErrBuilder, errorMessages, "building is not yet done")
}
return tb.tree, nil
}
// BeginTagNode implements the Builder interface.
func (tb *KeyStringValueTreeBuilder) BeginTagNode(tag string) error {
if tb.done {
return errors.New(ErrBuilder, errorMessages, "building is already done")
}
switch {
case tb.tree == nil:
tb.stack = collections.NewStringStack(tag)
tb.tree = collections.NewKeyStringValueTree(tag, "", false)
default:
tb.stack.Push(tag)
changer := tb.tree.Create(tb.stack.All()...)
if err := changer.Error(); err != nil {
return errors.Annotate(err, ErrBuilder, errorMessages)
}
}
return nil
}
// EndTagNode implements the Builder interface.
func (tb *KeyStringValueTreeBuilder) EndTagNode() error {
if tb.done {
return errors.New(ErrBuilder, errorMessages, "building is already done")
}
_, err := tb.stack.Pop()
if tb.stack.Len() == 0 {
tb.done = true
}
return err
}
// TextNode implements the Builder interface.
func (tb *KeyStringValueTreeBuilder) TextNode(text string) error {
if tb.done {
return errors.New(ErrBuilder, errorMessages, "building is already done")
}
value, err := tb.tree.At(tb.stack.All()...).Value()
if err != nil {
return errors.Annotate(err, ErrBuilder, errorMessages)
}
if value != "" {
return errors.New(ErrBuilder, errorMessages, "node has multiple values")
}
text = strings.TrimSpace(text)
if text != "" {
_, err = tb.tree.At(tb.stack.All()...).SetValue(text)
}
return err
}
// RawNode implements the Builder interface.
func (tb *KeyStringValueTreeBuilder) RawNode(raw string) error {
return tb.TextNode(raw)
}
// CommentNode implements the Builder interface.
func (tb *KeyStringValueTreeBuilder) CommentNode(comment string) error {
if tb.done {
return errors.New(ErrBuilder, errorMessages, "building is already done")
}
return nil
}
// EOF
|