Added a SP element for outputting unprocessed code.
[wrw.git] / wrw / sp / util.py
CommitLineData
ff79cdbf
FT
1import cons
2
3def findnsnames(el):
4 names = {}
5 nid = [1]
6 def proc(el):
7 if isinstance(el, cons.element):
8 if el.ns not in names:
9 names[el.ns] = u"n" + unicode(nid[0])
10 nid[:] = [nid[0] + 1]
11 for ch in el.children:
12 proc(ch)
13 proc(el)
14 if None in names:
15 names[None] = None
16 else:
17 names[el.ns] = None
18 return names
19
20class formatter(object):
21 def __init__(self, out, root, nsnames=None, charset="utf-8", doctype=None):
22 self.root = root
23 if nsnames is None:
24 nsnames = findnsnames(root)
25 self.nsnames = nsnames
26 self.out = out
27 self.charset = charset
28 self.doctype = doctype
29
30 def write(self, text):
31 self.out.write(text.encode(self.charset))
32
33 def quotewrite(self, buf):
34 for ch in buf:
35 if ch == u'&':
36 self.write(u"&")
37 elif ch == u'<':
38 self.write(u"&lt;")
39 elif ch == u'>':
40 self.write(u"&gt;")
41 else:
42 self.write(ch)
43
44 def text(self, el):
45 self.quotewrite(el)
46
f3464a4a
FT
47 def rawcode(self, el):
48 self.write(el)
49
ff79cdbf
FT
50 def attrval(self, buf):
51 qc, qt = (u"'", u"&apos;") if u'"' in buf else (u'"', u"&quot;")
52 self.write(qc)
53 for ch in buf:
54 if ch == u'&':
55 self.write(u"&amp;")
56 elif ch == u'<':
57 self.write(u"&lt;")
58 elif ch == u'>':
59 self.write(u"&gt;")
60 elif ch == qc:
61 self.write(qt)
62 else:
63 self.write(ch)
64 self.write(qc)
65
66 def attr(self, k, v):
67 self.write(k)
68 self.write(u'=')
69 self.attrval(v)
70
71 def shorttag(self, el, **extra):
72 self.write(u'<' + self.elname(el))
73 for k, v in el.attrs.iteritems():
74 self.write(u' ')
75 self.attr(k, v)
76 for k, v in extra.iteritems():
77 self.write(u' ')
78 self.attr(k, v)
79 self.write(u" />")
80
81 def elname(self, el):
82 ns = self.nsnames[el.ns]
83 if ns is None:
84 return el.name
85 else:
86 return ns + u':' + el.name
87
88 def starttag(self, el, **extra):
89 self.write(u'<' + self.elname(el))
90 for k, v in el.attrs.iteritems():
91 self.write(u' ')
92 self.attr(k, v)
93 for k, v in extra.iteritems():
94 self.write(u' ')
95 self.attr(k, v)
96 self.write(u'>')
97
98 def endtag(self, el):
99 self.write(u'</' + self.elname(el) + u'>')
100
101 def longtag(self, el):
102 self.starttag(el, **extra)
103 for ch in el.children:
104 self.node(ch)
105 self.endtag(el)
106
107 def element(self, el, **extra):
108 if len(el.children) == 0:
109 self.shorttag(el, **extra)
110 else:
111 self.longtag(el, **extra)
112
113 def node(self, el):
114 if isinstance(el, cons.element):
115 self.element(el)
116 elif isinstance(el, cons.text):
117 self.text(el)
f3464a4a
FT
118 elif isinstance(el, cons.raw):
119 self.rawcode(el)
ff79cdbf
FT
120 else:
121 raise Exception("Unknown object in element tree: " + el)
122
123 def start(self):
124 self.write(u'<?xml version="1.0" encoding="' + self.charset + u'" ?>\n')
125 if self.doctype:
126 self.write(u'<!DOCTYPE %s PUBLIC "%s" "%s">\n' % (self.root.name,
127 self.doctype[0],
128 self.doctype[1]))
129 extra = {}
130 for uri, nm in self.nsnames.iteritems():
131 if uri is None:
132 continue
133 if nm is None:
134 extra[u"xmlns"] = uri
135 else:
136 extra[u"xmlns:" + nm] = uri
137 self.element(self.root, **extra)
138
139 @classmethod
140 def output(cls, out, el, *args, **kw):
141 cls(out=out, root=el, *args, **kw).start()
142
143 def update(self, **ch):
144 ret = type(self).__new__(type(self))
145 ret.__dict__.update(self.__dict__)
146 ret.__dict__.update(ch)
147 return ret
148
149class iwriter(object):
150 def __init__(self, out):
151 self.out = out
152 self.atbol = True
153 self.col = 0
154
155 def write(self, buf):
156 for c in buf:
157 if c == '\n':
158 self.col = 0
159 else:
160 self.col += 1
161 self.out.write(c)
162 self.atbol = False
163
164 def indent(self, indent):
165 if self.atbol:
166 return
167 if self.col != 0:
168 self.write('\n')
169 self.write(indent)
170 self.atbol = True
171
172class indenter(formatter):
173 def __init__(self, indent=u" ", *args, **kw):
174 super(indenter, self).__init__(*args, **kw)
175 self.out = iwriter(self.out)
176 self.indent = indent
177 self.curind = u""
178
179 def simple(self, el):
180 for ch in el.children:
181 if not isinstance(ch, cons.text):
182 return False
183 return True
184
185 def longtag(self, el, **extra):
186 self.starttag(el, **extra)
187 sub = self
188 reind = False
189 if not self.simple(el):
190 sub = self.update(curind=self.curind + self.indent)
ee213a9b 191 sub.reindent()
ff79cdbf
FT
192 reind = True
193 for ch in el.children:
194 sub.node(ch)
195 if reind:
ee213a9b 196 self.reindent()
ff79cdbf
FT
197 self.endtag(el)
198
199 def element(self, el, **extra):
200 super(indenter, self).element(el, **extra)
201 if self.out.col > 80 and self.simple(el):
ee213a9b
FT
202 self.reindent()
203
204 def reindent(self):
205 self.out.indent(self.curind.encode(self.charset))
ff79cdbf
FT
206
207 def start(self):
208 super(indenter, self).start()
209 self.write('\n')