blob: 48372ea35512ae25377a81591f393e927c129777 [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)
Behdad Esfahbod14fb0312013-11-27 05:47:34 -050061 lines = data.split("\n")
Just7842e561999-12-16 21:34:53 +000062 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):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -050069 attrdata = self.stringifyattrs(*args, **kwargs)
Just7842e561999-12-16 21:34:53 +000070 data = "<%s%s/>" % (_TAG_, attrdata)
71 self.writeraw(data)
72
73 def begintag(self, _TAG_, *args, **kwargs):
Behdad Esfahbod66214cb2013-11-27 02:18:18 -050074 attrdata = self.stringifyattrs(*args, **kwargs)
Just7842e561999-12-16 21:34:53 +000075 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
Behdad Esfahbodac1b4352013-11-27 04:15:34 -0500111 attributes = sorted(kwargs.items())
Just7842e561999-12-16 21:34:53 +0000112 elif args:
113 assert len(args) == 1
114 attributes = args[0]
115 else:
116 return ""
117 data = ""
118 for attr, value in attributes:
119 data = data + ' %s="%s"' % (attr, escapeattr(str(value)))
120 return data
121
122
123def escape(data):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500124 data = data.replace("&", "&amp;")
125 data = data.replace("<", "&lt;")
Just7842e561999-12-16 21:34:53 +0000126 return data
127
128def escapeattr(data):
Behdad Esfahbod14fb0312013-11-27 05:47:34 -0500129 data = data.replace("&", "&amp;")
130 data = data.replace("<", "&lt;")
131 data = data.replace('"', "&quot;")
Just7842e561999-12-16 21:34:53 +0000132 return data
133
134def escape8bit(data):
135 def escapechar(c):
136 n = ord(c)
137 if c in "<&":
138 if c == "&":
139 return "&amp;"
140 else:
141 return "&lt;"
142 elif 32 <= n <= 127:
143 return c
144 else:
Behdad Esfahboddc7e6f32013-11-27 02:44:56 -0500145 return "&#" + repr(n) + ";"
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500146 return "".join(map(escapechar, data))
Just7842e561999-12-16 21:34:53 +0000147
148needswap = struct.pack("h", 1) == "\001\000"
149
150def escape16bit(data):
151 import array
152 a = array.array("H")
153 a.fromstring(data)
154 if needswap:
155 a.byteswap()
156 def escapenum(n, amp=ord("&"), lt=ord("<")):
157 if n == amp:
158 return "&amp;"
159 elif n == lt:
160 return "&lt;"
161 elif 32 <= n <= 127:
162 return chr(n)
163 else:
Behdad Esfahboddc7e6f32013-11-27 02:44:56 -0500164 return "&#" + repr(n) + ";"
Behdad Esfahbode5ca7962013-11-27 04:38:16 -0500165 return "".join(map(escapenum, a))
Just7842e561999-12-16 21:34:53 +0000166
167
168def hexStr(s):
169 h = string.hexdigits
170 r = ''
171 for c in s:
172 i = ord(c)
173 r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
174 return r
175