File: builder.go

package info (click to toggle)
golang-github-tideland-golib 4.24.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,144 kB
  • sloc: makefile: 4
file content (198 lines) | stat: -rw-r--r-- 5,193 bytes parent folder | download | duplicates (2)
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