xmlWriter.py revision ac1b4359467ca3deab03186a15eae1d55eb35567
17842e56b97ce677b83bdab09cda48bc2d89ac75aJust"""xmlWriter.py -- Simple XML authoring class"""
27842e56b97ce677b83bdab09cda48bc2d89ac75aJust
37842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport string
47842e56b97ce677b83bdab09cda48bc2d89ac75aJustimport struct
5e414c92045cee859cd5bd76610c5819c5ec8c68bJustimport os
67842e56b97ce677b83bdab09cda48bc2d89ac75aJust
77842e56b97ce677b83bdab09cda48bc2d89ac75aJustINDENT = "  "
87842e56b97ce677b83bdab09cda48bc2d89ac75aJust
981b0c2b659aec8df184d09c0d7ab069956b87b28jvr
107842e56b97ce677b83bdab09cda48bc2d89ac75aJustclass XMLWriter:
117842e56b97ce677b83bdab09cda48bc2d89ac75aJust
124b3df49b5b70b3618e91e3ab954cedca773831d6Behdad Esfahbod	def __init__(self, fileOrPath, indentwhite=INDENT, idlefunc=None, encoding="utf-8"):
1390beb95b77fdef99cad25354f36bf615d2042197jvr		if not hasattr(fileOrPath, "write"):
1490beb95b77fdef99cad25354f36bf615d2042197jvr			self.file = open(fileOrPath, "w")
157842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
167842e56b97ce677b83bdab09cda48bc2d89ac75aJust			# assume writable file object
1790beb95b77fdef99cad25354f36bf615d2042197jvr			self.file = fileOrPath
187842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.indentwhite = indentwhite
197842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.indentlevel = 0
207842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.stack = []
217842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.needindent = 1
2233f3327ad74d4550ee48ce85ce52a7374e55b997jvr		self.idlefunc = idlefunc
2333f3327ad74d4550ee48ce85ce52a7374e55b997jvr		self.idlecounter = 0
2481b0c2b659aec8df184d09c0d7ab069956b87b28jvr		if encoding:
2581b0c2b659aec8df184d09c0d7ab069956b87b28jvr			self.writeraw('<?xml version="1.0" encoding="%s"?>' % encoding)
2681b0c2b659aec8df184d09c0d7ab069956b87b28jvr		else:
2781b0c2b659aec8df184d09c0d7ab069956b87b28jvr			self.writeraw('<?xml version="1.0"?>')
287842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.newline()
297842e56b97ce677b83bdab09cda48bc2d89ac75aJust
307842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def close(self):
317842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.close()
327842e56b97ce677b83bdab09cda48bc2d89ac75aJust
337842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def write(self, data):
347842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.writeraw(escape(data))
357842e56b97ce677b83bdab09cda48bc2d89ac75aJust
367842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def write_noindent(self, data):
377842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.write(escape(data))
387842e56b97ce677b83bdab09cda48bc2d89ac75aJust
397842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def write8bit(self, data):
407842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.writeraw(escape8bit(data))
417842e56b97ce677b83bdab09cda48bc2d89ac75aJust
427842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def write16bit(self, data):
437842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.writeraw(escape16bit(data))
447842e56b97ce677b83bdab09cda48bc2d89ac75aJust
457842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def writeraw(self, data):
467842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if self.needindent:
477842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.file.write(self.indentlevel * self.indentwhite)
487842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.needindent = 0
497842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.write(data)
507842e56b97ce677b83bdab09cda48bc2d89ac75aJust
517842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def newline(self):
527842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.file.write("\n")
537842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.needindent = 1
5433f3327ad74d4550ee48ce85ce52a7374e55b997jvr		idlecounter = self.idlecounter
5533f3327ad74d4550ee48ce85ce52a7374e55b997jvr		if not idlecounter % 100 and self.idlefunc is not None:
5633f3327ad74d4550ee48ce85ce52a7374e55b997jvr			self.idlefunc()
5733f3327ad74d4550ee48ce85ce52a7374e55b997jvr		self.idlecounter = idlecounter + 1
587842e56b97ce677b83bdab09cda48bc2d89ac75aJust
597842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def comment(self, data):
607842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = escape(data)
617842e56b97ce677b83bdab09cda48bc2d89ac75aJust		lines = string.split(data, "\n")
627842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.writeraw("<!-- " + lines[0])
637842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for line in lines[1:]:
647842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.newline()
657842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.writeraw("     " + line)
667842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.writeraw(" -->")
677842e56b97ce677b83bdab09cda48bc2d89ac75aJust
687842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def simpletag(self, _TAG_, *args, **kwargs):
6966214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod		attrdata = self.stringifyattrs(*args, **kwargs)
707842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = "<%s%s/>" % (_TAG_, attrdata)
717842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.writeraw(data)
727842e56b97ce677b83bdab09cda48bc2d89ac75aJust
737842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def begintag(self, _TAG_, *args, **kwargs):
7466214cbe8c220625e61a85f386756c6de4ec82b2Behdad Esfahbod		attrdata = self.stringifyattrs(*args, **kwargs)
757842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = "<%s%s>" % (_TAG_, attrdata)
767842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.writeraw(data)
777842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.stack.append(_TAG_)
787842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.indent()
797842e56b97ce677b83bdab09cda48bc2d89ac75aJust
807842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def endtag(self, _TAG_):
817842e56b97ce677b83bdab09cda48bc2d89ac75aJust		assert self.stack and self.stack[-1] == _TAG_, "nonmatching endtag"
827842e56b97ce677b83bdab09cda48bc2d89ac75aJust		del self.stack[-1]
837842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.dedent()
847842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = "</%s>" % _TAG_
857842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.writeraw(data)
867842e56b97ce677b83bdab09cda48bc2d89ac75aJust
877842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def dumphex(self, data):
887842e56b97ce677b83bdab09cda48bc2d89ac75aJust		linelength = 16
897842e56b97ce677b83bdab09cda48bc2d89ac75aJust		hexlinelength = linelength * 2
907842e56b97ce677b83bdab09cda48bc2d89ac75aJust		chunksize = 8
917842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for i in range(0, len(data), linelength):
927842e56b97ce677b83bdab09cda48bc2d89ac75aJust			hexline = hexStr(data[i:i+linelength])
937842e56b97ce677b83bdab09cda48bc2d89ac75aJust			line = ""
947842e56b97ce677b83bdab09cda48bc2d89ac75aJust			white = ""
957842e56b97ce677b83bdab09cda48bc2d89ac75aJust			for j in range(0, hexlinelength, chunksize):
967842e56b97ce677b83bdab09cda48bc2d89ac75aJust				line = line + white + hexline[j:j+chunksize]
977842e56b97ce677b83bdab09cda48bc2d89ac75aJust				white = " "
987842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.writeraw(line)
997842e56b97ce677b83bdab09cda48bc2d89ac75aJust			self.newline()
1007842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1017842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def indent(self):
1027842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.indentlevel = self.indentlevel + 1
1037842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1047842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def dedent(self):
1057842e56b97ce677b83bdab09cda48bc2d89ac75aJust		assert self.indentlevel > 0
1067842e56b97ce677b83bdab09cda48bc2d89ac75aJust		self.indentlevel = self.indentlevel - 1
1077842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1087842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def stringifyattrs(self, *args, **kwargs):
1097842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if kwargs:
1107842e56b97ce677b83bdab09cda48bc2d89ac75aJust			assert not args
111ac1b4359467ca3deab03186a15eae1d55eb35567Behdad Esfahbod			attributes = sorted(kwargs.items())
1127842e56b97ce677b83bdab09cda48bc2d89ac75aJust		elif args:
1137842e56b97ce677b83bdab09cda48bc2d89ac75aJust			assert len(args) == 1
1147842e56b97ce677b83bdab09cda48bc2d89ac75aJust			attributes = args[0]
1157842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
1167842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return ""
1177842e56b97ce677b83bdab09cda48bc2d89ac75aJust		data = ""
1187842e56b97ce677b83bdab09cda48bc2d89ac75aJust		for attr, value in attributes:
1197842e56b97ce677b83bdab09cda48bc2d89ac75aJust			data = data + ' %s="%s"' % (attr, escapeattr(str(value)))
1207842e56b97ce677b83bdab09cda48bc2d89ac75aJust		return data
1217842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1227842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1237842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef escape(data):
1247842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = string.replace(data, "&", "&amp;")
1257842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = string.replace(data, "<", "&lt;")
1267842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return data
1277842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1287842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef escapeattr(data):
1297842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = string.replace(data, "&", "&amp;")
1307842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = string.replace(data, "<", "&lt;")
1317842e56b97ce677b83bdab09cda48bc2d89ac75aJust	data = string.replace(data, '"', "&quot;")
1327842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return data
1337842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1347842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef escape8bit(data):
1357842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def escapechar(c):
1367842e56b97ce677b83bdab09cda48bc2d89ac75aJust		n = ord(c)
1377842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if c in "<&":
1387842e56b97ce677b83bdab09cda48bc2d89ac75aJust			if c == "&":
1397842e56b97ce677b83bdab09cda48bc2d89ac75aJust				return "&amp;"
1407842e56b97ce677b83bdab09cda48bc2d89ac75aJust			else:
1417842e56b97ce677b83bdab09cda48bc2d89ac75aJust				return "&lt;"
1427842e56b97ce677b83bdab09cda48bc2d89ac75aJust		elif 32 <= n <= 127:
1437842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return c
1447842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
145dc7e6f3e5563a853477ebe26166b002c158dbe8bBehdad Esfahbod			return "&#" + repr(n) + ";"
1467842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return string.join(map(escapechar, data), "")
1477842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1487842e56b97ce677b83bdab09cda48bc2d89ac75aJustneedswap = struct.pack("h", 1) == "\001\000"
1497842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1507842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef escape16bit(data):
1517842e56b97ce677b83bdab09cda48bc2d89ac75aJust	import array
1527842e56b97ce677b83bdab09cda48bc2d89ac75aJust	a = array.array("H")
1537842e56b97ce677b83bdab09cda48bc2d89ac75aJust	a.fromstring(data)
1547842e56b97ce677b83bdab09cda48bc2d89ac75aJust	if needswap:
1557842e56b97ce677b83bdab09cda48bc2d89ac75aJust		a.byteswap()
1567842e56b97ce677b83bdab09cda48bc2d89ac75aJust	def escapenum(n, amp=ord("&"), lt=ord("<")):
1577842e56b97ce677b83bdab09cda48bc2d89ac75aJust		if n == amp:
1587842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return "&amp;"
1597842e56b97ce677b83bdab09cda48bc2d89ac75aJust		elif n == lt:
1607842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return "&lt;"
1617842e56b97ce677b83bdab09cda48bc2d89ac75aJust		elif 32 <= n <= 127:
1627842e56b97ce677b83bdab09cda48bc2d89ac75aJust			return chr(n)
1637842e56b97ce677b83bdab09cda48bc2d89ac75aJust		else:
164dc7e6f3e5563a853477ebe26166b002c158dbe8bBehdad Esfahbod			return "&#" + repr(n) + ";"
1657842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return string.join(map(escapenum, a), "")
1667842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1677842e56b97ce677b83bdab09cda48bc2d89ac75aJust
1687842e56b97ce677b83bdab09cda48bc2d89ac75aJustdef hexStr(s):
1697842e56b97ce677b83bdab09cda48bc2d89ac75aJust	h = string.hexdigits
1707842e56b97ce677b83bdab09cda48bc2d89ac75aJust	r = ''
1717842e56b97ce677b83bdab09cda48bc2d89ac75aJust	for c in s:
1727842e56b97ce677b83bdab09cda48bc2d89ac75aJust		i = ord(c)
1737842e56b97ce677b83bdab09cda48bc2d89ac75aJust		r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
1747842e56b97ce677b83bdab09cda48bc2d89ac75aJust	return r
1757842e56b97ce677b83bdab09cda48bc2d89ac75aJust
176