blob: aa70f13bcf9043efead496425a08bc875dff24a0 [file] [log] [blame]
Just7842e561999-12-16 21:34:53 +00001"""xmlWriter.py -- Simple XML authoring class"""
2
Just7842e561999-12-16 21:34:53 +00003import string
4import struct
Juste414c922000-01-04 13:51:59 +00005import os
Just7842e561999-12-16 21:34:53 +00006
7INDENT = " "
8
jvr81b0c2b2002-09-09 18:17:12 +00009
Just7842e561999-12-16 21:34:53 +000010class XMLWriter:
11
Behdad Esfahbod4b3df492013-08-20 15:29:19 -040012 def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf-8"):
jvr90beb952005-01-17 21:34:06 +000013 if not hasattr(fileOrPath, "write"):
14 self.file = open(fileOrPath, "w")
Just7842e561999-12-16 21:34:53 +000015 else:
16 # assume writable file object
jvr90beb952005-01-17 21:34:06 +000017 self.file = fileOrPath
Just7842e561999-12-16 21:34:53 +000018 self.indentwhite = indentwhite
19 self.indentlevel = 0
20 self.stack = []
21 self.needindent = 1
jvr33f33272002-07-23 16:41:08 +000022 self.idlefunc = idlefunc
23 self.idlecounter = 0
jvr81b0c2b2002-09-09 18:17:12 +000024 if encoding:
25 self.writeraw('<?xml version="1.0" encoding="%s"?>' % encoding)
26 else:
27 self.writeraw('<?xml version="1.0"?>')
Just7842e561999-12-16 21:34:53 +000028 self.newline()
Just7842e561999-12-16 21:34:53 +000029
30 def close(self):
31 self.file.close()
32
33 def write(self, data):
34 self.writeraw(escape(data))
35
36 def write_noindent(self, data):
37 self.file.write(escape(data))
38
39 def write8bit(self, data):
40 self.writeraw(escape8bit(data))
41
42 def write16bit(self, data):
43 self.writeraw(escape16bit(data))
44
45 def writeraw(self, data):
46 if self.needindent:
47 self.file.write(self.indentlevel * self.indentwhite)
48 self.needindent = 0
49 self.file.write(data)
50
51 def newline(self):
52 self.file.write("\n")
53 self.needindent = 1
jvr33f33272002-07-23 16:41:08 +000054 idlecounter = self.idlecounter
55 if not idlecounter % 100 and self.idlefunc is not None:
56 self.idlefunc()
57 self.idlecounter = idlecounter + 1
Just7842e561999-12-16 21:34:53 +000058
59 def comment(self, data):
60 data = escape(data)
61 lines = string.split(data, "\n")
62 self.writeraw("<!-- " + lines[0])
63 for line in lines[1:]:
64 self.newline()
65 self.writeraw(" " + line)
66 self.writeraw(" -->")
67
68 def simpletag(self, _TAG_, *args, **kwargs):
69 attrdata = apply(self.stringifyattrs, args, kwargs)
70 data = "<%s%s/>" % (_TAG_, attrdata)
71 self.writeraw(data)
72
73 def begintag(self, _TAG_, *args, **kwargs):
74 attrdata = apply(self.stringifyattrs, args, kwargs)
75 data = "<%s%s>" % (_TAG_, attrdata)
76 self.writeraw(data)
77 self.stack.append(_TAG_)
78 self.indent()
79
80 def endtag(self, _TAG_):
81 assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
82 del self.stack[-1]
83 self.dedent()
84 data = "</%s>" % _TAG_
85 self.writeraw(data)
86
87 def dumphex(self, data):
88 linelength = 16
89 hexlinelength = linelength * 2
90 chunksize = 8
91 for i in range(0, len(data), linelength):
92 hexline = hexStr(data[i:i+linelength])
93 line = ""
94 white = ""
95 for j in range(0, hexlinelength, chunksize):
96 line = line + white + hexline[j:j+chunksize]
97 white = " "
98 self.writeraw(line)
99 self.newline()
100
101 def indent(self):
102 self.indentlevel = self.indentlevel + 1
103
104 def dedent(self):
105 assert self.indentlevel > 0
106 self.indentlevel = self.indentlevel - 1
107
108 def stringifyattrs(self, *args, **kwargs):
109 if kwargs:
110 assert not args
111 attributes = kwargs.items()
112 attributes.sort()
113 elif args:
114 assert len(args) == 1
115 attributes = args[0]
116 else:
117 return ""
118 data = ""
119 for attr, value in attributes:
120 data = data + ' %s="%s"' % (attr, escapeattr(str(value)))
121 return data
122
123
124def escape(data):
125 data = string.replace(data, "&", "&amp;")
126 data = string.replace(data, "<", "&lt;")
127 return data
128
129def escapeattr(data):
130 data = string.replace(data, "&", "&amp;")
131 data = string.replace(data, "<", "&lt;")
132 data = string.replace(data, '"', "&quot;")
133 return data
134
135def escape8bit(data):
136 def escapechar(c):
137 n = ord(c)
138 if c in "<&":
139 if c == "&":
140 return "&amp;"
141 else:
142 return "&lt;"
143 elif 32 <= n <= 127:
144 return c
145 else:
146 return "&#" + `n` + ";"
147 return string.join(map(escapechar, data), "")
148
149needswap = struct.pack("h", 1) == "\001\000"
150
151def escape16bit(data):
152 import array
153 a = array.array("H")
154 a.fromstring(data)
155 if needswap:
156 a.byteswap()
157 def escapenum(n, amp=ord("&"), lt=ord("<")):
158 if n == amp:
159 return "&amp;"
160 elif n == lt:
161 return "&lt;"
162 elif 32 <= n <= 127:
163 return chr(n)
164 else:
165 return "&#" + `n` + ";"
166 return string.join(map(escapenum, a), "")
167
168
169def hexStr(s):
170 h = string.hexdigits
171 r = ''
172 for c in s:
173 i = ord(c)
174 r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
175 return r
176