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