22
22
# importing this module is fairly slow because it has to load several
25
from bzrlib.trace import mutter, warning
29
# it's in this package in python2.5
30
from xml.etree.cElementTree import (ElementTree, SubElement, Element,
31
XMLTreeBuilder, fromstring, tostring)
32
import xml.etree as elementtree
34
from cElementTree import (ElementTree, SubElement, Element,
35
XMLTreeBuilder, fromstring, tostring)
37
ParseError = SyntaxError
26
from util.cElementTree import ElementTree, SubElement, Element
38
27
except ImportError:
39
mutter('WARNING: using slower ElementTree; consider installing cElementTree'
40
" and make sure it's on your PYTHONPATH")
41
# this copy is shipped with bzr
42
from util.elementtree.ElementTree import (ElementTree, SubElement,
43
Element, XMLTreeBuilder,
45
import util.elementtree as elementtree
46
from xml.parsers.expat import ExpatError as ParseError
28
from util.elementtree.ElementTree import ElementTree, SubElement, Element
48
from bzrlib import errors
30
from bzrlib.inventory import ROOT_ID, Inventory, InventoryEntry
31
from bzrlib.revision import Revision, RevisionReference
32
from bzrlib.errors import BzrError
51
35
class Serializer(object):
55
39
elt = self._pack_inventory(inv)
56
40
self._write_element(elt, f)
58
def write_inventory_to_string(self, inv):
59
return tostring(self._pack_inventory(inv)) + '\n'
61
def read_inventory_from_string(self, xml_string):
63
return self._unpack_inventory(fromstring(xml_string))
65
raise errors.UnexpectedInventoryFormat(e)
67
42
def read_inventory(self, f):
69
return self._unpack_inventory(self._read_element(f))
71
raise errors.UnexpectedInventoryFormat(e)
43
return self._unpack_inventory(self._read_element(f))
73
45
def write_revision(self, rev, f):
74
46
self._write_element(self._pack_revision(rev), f)
76
def write_revision_to_string(self, rev):
77
return tostring(self._pack_revision(rev)) + '\n'
79
48
def read_revision(self, f):
80
49
return self._unpack_revision(self._read_element(f))
82
def read_revision_from_string(self, xml_string):
83
return self._unpack_revision(fromstring(xml_string))
85
51
def _write_element(self, elt, f):
86
52
ElementTree(elt).write(f, 'utf-8')
90
56
return ElementTree().parse(f)
93
# performance tuning for elementree's serialiser. This should be
94
# sent upstream - RBC 20060523.
95
# the functions here are patched into elementtree at runtime.
97
escape_re = re.compile("[&'\"<>]")
100
"'":"'", # FIXME: overkill
105
def _escape_replace(match, map=escape_map):
106
return map[match.group()]
108
def _escape_attrib(text, encoding=None, replace=None):
109
# escape attribute value
113
text = elementtree.ElementTree._encode(text, encoding)
115
return elementtree.ElementTree._encode_entity(text)
117
return escape_re.sub(_escape_replace, text)
119
text = replace(text, "&", "&")
120
text = replace(text, "'", "'") # FIXME: overkill
121
text = replace(text, "\"", """)
122
text = replace(text, "<", "<")
123
text = replace(text, ">", ">")
125
except (TypeError, AttributeError):
126
elementtree.ElementTree._raise_serialization_error(text)
128
elementtree.ElementTree._escape_attrib = _escape_attrib
130
escape_cdata_re = re.compile("[&<>]")
136
def _escape_cdata_replace(match, map=escape_cdata_map):
137
return map[match.group()]
139
def _escape_cdata(text, encoding=None, replace=None):
140
# escape character data
144
text = elementtree.ElementTree._encode(text, encoding)
146
return elementtree.ElementTree._encode_entity(text)
148
return escape_cdata_re.sub(_escape_cdata_replace, text)
150
text = replace(text, "&", "&")
151
text = replace(text, "<", "<")
152
text = replace(text, ">", ">")
154
except (TypeError, AttributeError):
155
elementtree.ElementTree._raise_serialization_error(text)
157
elementtree.ElementTree._escape_cdata = _escape_cdata
60
class _Serializer_v4(Serializer):
61
"""Version 0.0.4 serializer
63
You should use the serialzer_v4 singleton."""
67
def _pack_inventory(self, inv):
68
"""Convert to XML Element"""
69
e = Element('inventory')
71
if inv.root.file_id not in (None, ROOT_ID):
72
e.set('file_id', inv.root.file_id)
73
for path, ie in inv.iter_entries():
74
e.append(self._pack_entry(ie))
78
def _pack_entry(self, ie):
79
"""Convert InventoryEntry to XML element"""
81
e.set('name', ie.name)
82
e.set('file_id', ie.file_id)
83
e.set('kind', ie.kind)
85
if ie.text_size != None:
86
e.set('text_size', '%d' % ie.text_size)
88
for f in ['text_id', 'text_sha1']:
93
# to be conservative, we don't externalize the root pointers
94
# for now, leaving them as null in the xml form. in a future
95
# version it will be implied by nested elements.
96
if ie.parent_id != ROOT_ID:
97
assert isinstance(ie.parent_id, basestring)
98
e.set('parent_id', ie.parent_id)
105
def _unpack_inventory(self, elt):
106
"""Construct from XML Element
108
assert elt.tag == 'inventory'
109
root_id = elt.get('file_id') or ROOT_ID
110
inv = Inventory(root_id)
112
ie = self._unpack_entry(e)
113
if ie.parent_id == ROOT_ID:
114
ie.parent_id = root_id
119
def _unpack_entry(self, elt):
120
assert elt.tag == 'entry'
122
## original format inventories don't have a parent_id for
123
## nodes in the root directory, but it's cleaner to use one
125
parent_id = elt.get('parent_id')
126
if parent_id == None:
129
ie = InventoryEntry(elt.get('file_id'),
133
ie.text_id = elt.get('text_id')
134
ie.text_sha1 = elt.get('text_sha1')
136
## mutter("read inventoryentry: %r" % (elt.attrib))
138
v = elt.get('text_size')
139
ie.text_size = v and int(v)
144
def _pack_revision(self, rev):
145
"""Revision object -> xml tree"""
146
root = Element('revision',
147
committer = rev.committer,
148
timestamp = '%.9f' % rev.timestamp,
149
revision_id = rev.revision_id,
150
inventory_id = rev.inventory_id,
151
inventory_sha1 = rev.inventory_sha1,
154
root.set('timezone', str(rev.timezone))
157
msg = SubElement(root, 'message')
158
msg.text = rev.message
162
pelts = SubElement(root, 'parents')
163
pelts.tail = pelts.text = '\n'
164
for rr in rev.parents:
165
assert isinstance(rr, RevisionReference)
166
p = SubElement(pelts, 'revision_ref')
168
assert rr.revision_id
169
p.set('revision_id', rr.revision_id)
171
p.set('revision_sha1', rr.revision_sha1)
176
def _unpack_revision(self, elt):
177
"""XML Element -> Revision object"""
179
# <changeset> is deprecated...
180
if elt.tag not in ('revision', 'changeset'):
181
raise BzrError("unexpected tag in revision file: %r" % elt)
183
rev = Revision(committer = elt.get('committer'),
184
timestamp = float(elt.get('timestamp')),
185
revision_id = elt.get('revision_id'),
186
inventory_id = elt.get('inventory_id'),
187
inventory_sha1 = elt.get('inventory_sha1')
190
precursor = elt.get('precursor')
191
precursor_sha1 = elt.get('precursor_sha1')
193
pelts = elt.find('parents')
197
assert p.tag == 'revision_ref', \
198
"bad parent node tag %r" % p.tag
199
rev_ref = RevisionReference(p.get('revision_id'),
200
p.get('revision_sha1'))
201
rev.parents.append(rev_ref)
205
prec_parent = rev.parents[0].revision_id
206
assert prec_parent == precursor
208
# revisions written prior to 0.0.5 have a single precursor
209
# give as an attribute
210
rev_ref = RevisionReference(precursor, precursor_sha1)
211
rev.parents.append(rev_ref)
213
v = elt.get('timezone')
214
rev.timezone = v and int(v)
216
rev.message = elt.findtext('message') # text of <message>
222
"""singleton instance"""
223
serializer_v4 = _Serializer_v4()