1
2 r"""
3 :Copyright:
4
5 Copyright 2006 - 2015
6 Andr\xe9 Malo or his licensors, as applicable
7
8 :License:
9
10 Licensed under the Apache License, Version 2.0 (the "License");
11 you may not use this file except in compliance with the License.
12 You may obtain a copy of the License at
13
14 http://www.apache.org/licenses/LICENSE-2.0
15
16 Unless required by applicable law or agreed to in writing, software
17 distributed under the License is distributed on an "AS IS" BASIS,
18 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 See the License for the specific language governing permissions and
20 limitations under the License.
21
22 ========================
23 Template Builder Logic
24 ========================
25
26 This module provides the logic to build a nodetree out of parser
27 events.
28 """
29 if __doc__:
30
31 __doc__ = __doc__.encode('ascii').decode('unicode_escape')
32 __author__ = r"Andr\xe9 Malo".encode('ascii').decode('unicode_escape')
33 __docformat__ = "restructuredtext en"
34
35 import codecs as _codecs
36
37 from ..._exceptions import TemplateEncodingError
38 from ... import interfaces as _interfaces
39 from ... import nodetree as _nodetree
40 from .. import _analyzer
41
42
43 -class TextBuilder(object):
44 """
45 Text Template tree builder
46
47 :IVariables:
48 `_tree` : `nodetree.Root`
49 The built subtree
50
51 `_text` : ``list``
52 The current text buffer
53
54 `_tagstack` : ``list``
55 The stack of currently nested tag names with associated nodes
56
57 `_nodestack` : ``list``
58 The stack of currently nested snippet parameters
59
60 `_devnull` : ``bool``
61 Are we inside a removed element?
62
63 `encoding` : ``str``
64 Template encoding
65
66 `encoder` : `EncoderInterface`
67 Encoder
68
69 `decoder` : `DecoderInterface`
70 Decoder
71
72 `analyze` : `AttributeAnalyzerInterface`
73 Attribute analyzer
74 """
75 __implements__ = [_interfaces.BuilderInterface,
76 _interfaces.BuildingListenerInterface]
77
78 encoding = 'utf-8'
79
80 - def __init__(self, encoder, decoder, analyzer=None):
81 """
82 Initialization
83
84 :Parameters:
85 `encoder` : ``callable``
86 Encoder factory
87
88 `decoder` : ``callable``
89 Decoder factory
90
91 `analyzer` : `AttributeAnalyzerInterface`
92 Attribute analyzer
93 """
94 root = _nodetree.Root()
95 self._tree = root
96 self._text = []
97 self._tagstack = []
98 self._nodestack = [root]
99 self._devnull = False
100 self.encoder = encoder(self.encoding)
101 self.decoder = decoder(self.encoding)
102 if analyzer is None:
103 analyzer = _analyzer.DEFAULT_ANALYZER(self.decoder, hidden=True)
104 self.analyze = analyzer
105
106 - def _flush_text(self):
107 """ Flush current text buffer """
108 if self._text:
109 if not self._devnull:
110 self._nodestack[-1].append_text(''.join(self._text))
111 self._text = []
112
113
114
115
116
117 - def handle_text(self, data):
118 """ :see: `ListenerInterface` """
119 if not self._devnull:
120 self._text.append(data)
121
122 - def handle_escape(self, escaped, data):
123 """ :see: `ListenerInterface` """
124 if not self._devnull:
125 self._flush_text()
126 self._nodestack[-1].append_escape(escaped, data)
127
128 - def handle_starttag(self, name, attr, closed, data):
129 """ :see: `ListenerInterface` """
130 starttag = self.decoder.normalize(name)
131
132 if not self._devnull:
133 attr, special = self.analyze(attr, name=name)
134 if special:
135 self._flush_text()
136 flags, tdi = special.get('attribute', ('', None))
137 if not closed and tdi is None and flags == '-':
138 self._devnull = True
139 self._tagstack.append((starttag, '-'))
140 self._nodestack.append('-')
141 return
142
143 node = self._nodestack[-1].append_node(
144 name, attr, special, closed
145 )
146 if not closed:
147 self._tagstack.append((starttag, node))
148 self._nodestack.append(node)
149 return
150
151
152 if not closed and len(self._nodestack) > 1:
153
154 self._tagstack.append((starttag, None))
155 self.handle_text(data)
156
157 - def handle_endtag(self, name, data):
158 """ :see: `ListenerInterface` """
159 endtag = self.decoder.normalize(name)
160 tagstack = self._tagstack
161 if tagstack:
162 starttag, node = tagstack[-1]
163 if starttag == endtag:
164 tagstack.pop()
165
166
167 if node is not None:
168 self._flush_text()
169 node = self._nodestack.pop()
170 if self._devnull:
171 self._devnull = False
172 else:
173 node.endtag = data
174 return
175
176 self.handle_text(data)
177
179 """ :see: `ListenerInterface` """
180 pass
181
182 - def handle_pi(self, data):
183 """ :see: `ListenerInterface` """
184 pass
185
186 - def handle_msection(self, name, value, data):
187 """ :see: `ListenerInterface` """
188
189
190 self.handle_text(data)
191
192 - def handle_decl(self, name, value, data):
193 """ :see: `ListenerInterface` """
194
195
196 self.handle_text(data)
197
198
199
200
201
202 - def handle_encoding(self, encoding):
203 """
204 :See: `tdi.interfaces.BuildingListenerInterface`
205
206 :Exceptions:
207 - `TemplateEncodingError` : encoding was not recognized
208 """
209 try:
210 _codecs.lookup(encoding)
211 except LookupError, e:
212 raise TemplateEncodingError(str(e))
213 if self.encoding != encoding:
214 self.encoding = encoding
215 self.encoder.encoding = encoding
216 self.decoder.encoding = encoding
217
218
219
220
221
222 - def finalize(self):
223 """ :See: `tdi.interfaces.BuilderInterface` """
224 self._flush_text()
225 self._tree.finalize(self.encoder, self.decoder)
226 return self._tree
227