20
20
# "XML is like violence: if it doesn't solve your problem, you aren't
21
21
# using enough of it." -- various
23
# importing this module is fairly slow because it has to load several
23
# importing this module is fairly slow because it has to load several ElementTree bits
27
from util.cElementTree import ElementTree, SubElement, Element
25
from cElementTree import ElementTree, SubElement, Element
28
26
except ImportError:
29
from util.elementtree.ElementTree import ElementTree, SubElement, Element
31
from bzrlib.inventory import ROOT_ID, Inventory, InventoryEntry
32
from bzrlib.revision import Revision, RevisionReference
33
from bzrlib.errors import BzrError
36
class Serializer(object):
37
"""Abstract object serialize/deserialize"""
38
def write_inventory(self, inv, f):
39
"""Write inventory to a file"""
40
elt = self._pack_inventory(inv)
41
self._write_element(elt, f)
43
def read_inventory(self, f):
44
return self._unpack_inventory(self._read_element(f))
46
def write_revision(self, rev, f):
47
self._write_element(self._pack_revision(rev), f)
49
def read_revision(self, f):
50
return self._unpack_revision(self._read_element(f))
52
def _write_element(self, elt, f):
53
ElementTree(elt).write(f, 'utf-8')
56
def _read_element(self, f):
57
return ElementTree().parse(f)
61
class _Serializer_v4(Serializer):
62
"""Version 0.0.4 serializer
64
You should use the serialzer_v4 singleton."""
68
def _pack_inventory(self, inv):
69
"""Convert to XML Element"""
70
e = Element('inventory')
72
if inv.root.file_id not in (None, ROOT_ID):
73
e.set('file_id', inv.root.file_id)
74
for path, ie in inv.iter_entries():
75
e.append(self._pack_entry(ie))
79
def _pack_entry(self, ie):
80
"""Convert InventoryEntry to XML element"""
82
e.set('name', ie.name)
83
e.set('file_id', ie.file_id)
84
e.set('kind', ie.kind)
86
if ie.text_size != None:
87
e.set('text_size', '%d' % ie.text_size)
89
for f in ['text_id', 'text_sha1']:
94
# to be conservative, we don't externalize the root pointers
95
# for now, leaving them as null in the xml form. in a future
96
# version it will be implied by nested elements.
97
if ie.parent_id != ROOT_ID:
98
assert isinstance(ie.parent_id, basestring)
99
e.set('parent_id', ie.parent_id)
106
def _unpack_inventory(self, elt):
107
"""Construct from XML Element
109
assert elt.tag == 'inventory'
110
root_id = elt.get('file_id') or ROOT_ID
111
inv = Inventory(root_id)
113
ie = self._unpack_entry(e)
114
if ie.parent_id == ROOT_ID:
115
ie.parent_id = root_id
120
def _unpack_entry(self, elt):
121
assert elt.tag == 'entry'
123
## original format inventories don't have a parent_id for
124
## nodes in the root directory, but it's cleaner to use one
126
parent_id = elt.get('parent_id')
127
if parent_id == None:
130
ie = InventoryEntry(elt.get('file_id'),
134
ie.text_id = elt.get('text_id')
135
ie.text_sha1 = elt.get('text_sha1')
137
## mutter("read inventoryentry: %r" % (elt.attrib))
139
v = elt.get('text_size')
140
ie.text_size = v and int(v)
145
def _pack_revision(self, rev):
146
"""Revision object -> xml tree"""
147
root = Element('revision',
148
committer = rev.committer,
149
timestamp = '%.9f' % rev.timestamp,
150
revision_id = rev.revision_id,
151
inventory_id = rev.inventory_id,
152
inventory_sha1 = rev.inventory_sha1,
155
root.set('timezone', str(rev.timezone))
158
msg = SubElement(root, 'message')
159
msg.text = rev.message
163
pelts = SubElement(root, 'parents')
164
pelts.tail = pelts.text = '\n'
165
for rr in rev.parents:
166
assert isinstance(rr, RevisionReference)
167
p = SubElement(pelts, 'revision_ref')
169
assert rr.revision_id
170
p.set('revision_id', rr.revision_id)
172
p.set('revision_sha1', rr.revision_sha1)
177
def _unpack_revision(self, elt):
178
"""XML Element -> Revision object"""
180
# <changeset> is deprecated...
181
if elt.tag not in ('revision', 'changeset'):
182
raise BzrError("unexpected tag in revision file: %r" % elt)
184
rev = Revision(committer = elt.get('committer'),
185
timestamp = float(elt.get('timestamp')),
186
revision_id = elt.get('revision_id'),
187
inventory_id = elt.get('inventory_id'),
188
inventory_sha1 = elt.get('inventory_sha1')
191
precursor = elt.get('precursor')
192
precursor_sha1 = elt.get('precursor_sha1')
194
pelts = elt.find('parents')
198
assert p.tag == 'revision_ref', \
199
"bad parent node tag %r" % p.tag
200
rev_ref = RevisionReference(p.get('revision_id'),
201
p.get('revision_sha1'))
202
rev.parents.append(rev_ref)
206
prec_parent = rev.parents[0].revision_id
207
assert prec_parent == precursor
209
# revisions written prior to 0.0.5 have a single precursor
210
# give as an attribute
211
rev_ref = RevisionReference(precursor, precursor_sha1)
212
rev.parents.append(rev_ref)
214
v = elt.get('timezone')
215
rev.timezone = v and int(v)
217
rev.message = elt.findtext('message') # text of <message>
222
class _Serializer_v5(Serializer):
223
"""Version 5 serializer
225
Packs objects into XML and vice versa.
227
You should use the serialzer_v5 singleton."""
231
def _pack_inventory(self, inv):
232
"""Convert to XML Element"""
233
e = Element('inventory')
235
if inv.root.file_id not in (None, ROOT_ID):
236
e.set('file_id', inv.root.file_id)
237
for path, ie in inv.iter_entries():
238
e.append(self._pack_entry(ie))
242
def _pack_entry(self, ie):
243
"""Convert InventoryEntry to XML element"""
244
assert ie.kind == 'directory' or ie.kind == 'file'
246
e.set('name', ie.name)
247
e.set('file_id', ie.file_id)
249
if ie.text_size != None:
250
e.set('text_size', '%d' % ie.text_size)
252
for f in ['text_version', 'text_sha1', 'entry_version']:
257
# to be conservative, we don't externalize the root pointers
258
# for now, leaving them as null in the xml form. in a future
259
# version it will be implied by nested elements.
260
if ie.parent_id != ROOT_ID:
261
assert isinstance(ie.parent_id, basestring)
262
e.set('parent_id', ie.parent_id)
269
def _pack_revision(self, rev):
270
"""Revision object -> xml tree"""
271
root = Element('revision',
272
committer = rev.committer,
273
timestamp = '%.9f' % rev.timestamp,
274
revision_id = rev.revision_id,
275
inventory_id = rev.inventory_id,
276
inventory_sha1 = rev.inventory_sha1,
279
root.set('timezone', str(rev.timezone))
282
msg = SubElement(root, 'message')
283
msg.text = rev.message
287
pelts = SubElement(root, 'parents')
288
pelts.tail = pelts.text = '\n'
289
for rr in rev.parents:
290
assert isinstance(rr, RevisionReference)
291
p = SubElement(pelts, 'revision_ref')
293
assert rr.revision_id
294
p.set('revision_id', rr.revision_id)
300
def _unpack_inventory(self, elt):
301
"""Construct from XML Element
303
assert elt.tag == 'inventory'
304
root_id = elt.get('file_id') or ROOT_ID
305
inv = Inventory(root_id)
307
ie = self._unpack_entry(e)
308
if ie.parent_id == ROOT_ID:
309
ie.parent_id = root_id
314
def _unpack_entry(self, elt):
316
assert kind == 'directory' or kind == 'file'
318
parent_id = elt.get('parent_id')
319
if parent_id == None:
322
ie = InventoryEntry(elt.get('file_id'),
326
ie.text_version = elt.get('text_version')
327
ie.entry_version = elt.get('entry_version')
328
ie.text_sha1 = elt.get('text_sha1')
329
v = elt.get('text_size')
330
ie.text_size = v and int(v)
335
def _unpack_revision(self, elt):
336
"""XML Element -> Revision object"""
337
assert elt.tag == 'revision'
339
rev = Revision(committer = elt.get('committer'),
340
timestamp = float(elt.get('timestamp')),
341
revision_id = elt.get('revision_id'),
342
inventory_id = elt.get('inventory_id'),
343
inventory_sha1 = elt.get('inventory_sha1')
346
for p in elt.find('parents'):
347
assert p.tag == 'revision_ref', \
348
"bad parent node tag %r" % p.tag
349
rev_ref = RevisionReference(p.get('revision_id'))
350
rev.parents.append(rev_ref)
352
v = elt.get('timezone')
353
rev.timezone = v and int(v)
355
rev.message = elt.findtext('message') # text of <message>
360
"""singleton instance"""
361
serializer_v4 = _Serializer_v4()
363
serializer_v5 = _Serializer_v5()
27
from elementtree.ElementTree import ElementTree, SubElement, Element
31
"""Write object o to file f as XML.
33
o must provide a to_element method.
35
ElementTree(o.to_element()).write(f, 'utf-8')
39
def unpack_xml(cls, f):
40
return cls.from_element(ElementTree().parse(f))