bbcode - processor.go
1 package bbcode
2
3 import (
4 "bytes"
5 "io"
6 "strings"
7
8 "vimagination.zapto.org/parser"
9 )
10
11 // Processor contains methods necessary for creating custom Handler's.
12 type Processor struct {
13 w io.Writer
14 err error
15 p parser.Parser
16 bbCode *BBCode
17 }
18
19 // Write writes to the underlying writer.
20 // The error is stored and does not need to be handled.
21 func (p *Processor) Write(b []byte) (int, error) {
22 if p.err != nil {
23 return 0, p.err
24 }
25
26 var n int
27
28 n, p.err = p.w.Write(b)
29
30 return n, p.err
31 }
32
33 // Process will continue processing the bbCode until it gets to an end tag
34 // which matches the tag name given, or until it reaches the end of the input.
35 // It returns true if the end tag was found, or false otherwise.
36 func (p *Processor) Process(untilTag string) bool {
37 for {
38 switch t := p.Get().(type) {
39 case Text:
40 p.printText(t)
41 case OpenTag:
42 p.ProcessTag(t)
43 case CloseTag:
44 if strings.EqualFold(t.Name, untilTag) {
45 return true
46 }
47
48 p.printCloseTag(t)
49 default:
50 return false
51 }
52 }
53 }
54
55 // GetContents grabs the raw contents of a tag and returns it as a string.
56 func (p *Processor) GetContents(untilTag string) string {
57 if p.err != nil {
58 return ""
59 }
60
61 w := p.w
62 b := bytes.NewBuffer(make([]byte, 0, 1024))
63 p.w = b
64 t := p.bbCode.text
65 p.bbCode.text = PlainText
66
67 Loop:
68 for {
69 switch t := p.Get().(type) {
70 case Text:
71 p.printText(t)
72 case OpenTag:
73 p.printOpenTag(t)
74 case CloseTag:
75 if strings.EqualFold(t.Name, untilTag) {
76 break Loop
77 }
78
79 p.printCloseTag(t)
80 default:
81 break Loop
82 }
83 }
84
85 p.bbCode.text = t
86 p.w = w
87
88 return b.String()
89 }
90
91 // ProcessTag will process the given tag as normal.
92 func (p *Processor) ProcessTag(t OpenTag) {
93 h := p.getTagHandler(t.Name)
94
95 if h == nil {
96 p.printOpenTag(t)
97 } else {
98 var attr string
99
100 if t.Attr != nil {
101 attr = *t.Attr
102 }
103
104 h.Handle(p, attr)
105 }
106 }
107
108 func (p *Processor) getTagHandler(name string) Handler {
109 for _, tag := range p.bbCode.tags {
110 if strings.EqualFold(tag.Name(), name) {
111 return tag
112 }
113 }
114
115 return nil
116 }
117
118 // Get returns the next token.
119 // It will be either a Text, OpenTag or a CloseTag.
120 func (p *Processor) Get() interface{} {
121 phrase, _ := p.p.GetPhrase()
122
123 switch phrase.Type {
124 case phraseText:
125 text := make(Text, 0, len(phrase.Data))
126
127 for _, t := range phrase.Data {
128 text = append(text, t.Data)
129 }
130
131 return text
132 case phraseOpen:
133 tag := OpenTag{
134 Name: phrase.Data[0].Data,
135 }
136
137 if len(phrase.Data) > 1 {
138 tag.Attr = &phrase.Data[1].Data
139 }
140
141 return tag
142 case phraseClose:
143 return CloseTag{Name: phrase.Data[0].Data}
144 }
145
146 return nil
147 }
148
149 // Print writes the textual representation of the given token to the output,
150 // using the text Handler.
151 func (p *Processor) Print(t interface{}) {
152 switch t := t.(type) {
153 case string:
154 p.bbCode.text.Handle(p, t)
155 case Text:
156 p.printText(t)
157 case OpenTag:
158 p.printOpenTag(t)
159 case CloseTag:
160 p.printCloseTag(t)
161 }
162 }
163
164 func (p *Processor) printText(t Text) {
165 for _, str := range t {
166 p.bbCode.text.Handle(p, str)
167 }
168 }
169
170 func (p *Processor) printOpenTag(t OpenTag) {
171 p.bbCode.text.Handle(p, p.bbCode.tks.openTag)
172 p.bbCode.text.Handle(p, t.Name)
173
174 if t.Attr != nil {
175 p.bbCode.text.Handle(p, p.bbCode.tks.attributeSep)
176 p.bbCode.text.Handle(p, *t.Attr)
177 }
178
179 p.bbCode.text.Handle(p, p.bbCode.tks.closeTag)
180 }
181
182 func (p *Processor) printCloseTag(t CloseTag) {
183 p.bbCode.text.Handle(p, p.bbCode.tks.openTag)
184 p.bbCode.text.Handle(p, p.bbCode.tks.closingTag)
185 p.bbCode.text.Handle(p, t.Name)
186 p.bbCode.text.Handle(p, p.bbCode.tks.closeTag)
187 }
188
189 // Text is a token containing simple textual data.
190 type Text []string
191
192 // OpenTag is a token containing the name of the tag and a possible attribute..
193 type OpenTag struct {
194 Name string
195 Attr *string
196 }
197
198 // CloseTag is a token containing the name of the tag.
199 type CloseTag struct {
200 Name string
201 }
202