19
20
# "XML is like violence: if it doesn't solve your problem, you aren't
20
21
# using enough of it." -- various
22
# 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
26
from util.cElementTree import ElementTree, SubElement, Element
25
from cElementTree import ElementTree, SubElement, Element
27
26
except ImportError:
28
from util.elementtree.ElementTree import ElementTree, SubElement, Element
30
from bzrlib.inventory import ROOT_ID, Inventory, InventoryEntry
31
from bzrlib.revision import Revision, RevisionReference
32
from bzrlib.errors import BzrError
35
class Serializer(object):
36
"""Abstract object serialize/deserialize"""
37
def write_inventory(self, inv, f):
38
"""Write inventory to a file"""
39
elt = self._pack_inventory(inv)
40
self._write_element(elt, f)
42
def read_inventory(self, f):
43
return self._unpack_inventory(self._read_element(f))
45
def write_revision(self, rev, f):
46
self._write_element(self._pack_revision(rev), f)
48
def read_revision(self, f):
49
return self._unpack_revision(self._read_element(f))
51
def _write_element(self, elt, f):
52
ElementTree(elt).write(f, 'utf-8')
55
def _read_element(self, f):
56
return ElementTree().parse(f)
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>
221
class _Serializer_v5(Serializer):
222
"""Version 5 serializer
224
Packs objects into XML and vice versa.
226
You should use the serialzer_v5 singleton."""
230
def _pack_inventory(self, inv):
231
"""Convert to XML Element"""
232
e = Element('inventory')
234
if inv.root.file_id not in (None, ROOT_ID):
235
e.set('file_id', inv.root.file_id)
236
for path, ie in inv.iter_entries():
237
e.append(self._pack_entry(ie))
241
def _pack_entry(self, ie):
242
"""Convert InventoryEntry to XML element"""
243
assert ie.kind == 'directory' or ie.kind == 'file'
245
e.set('name', ie.name)
246
e.set('file_id', ie.file_id)
248
if ie.text_size != None:
249
e.set('text_size', '%d' % ie.text_size)
251
for f in ['text_version', 'text_sha1', 'entry_version']:
256
# to be conservative, we don't externalize the root pointers
257
# for now, leaving them as null in the xml form. in a future
258
# version it will be implied by nested elements.
259
if ie.parent_id != ROOT_ID:
260
assert isinstance(ie.parent_id, basestring)
261
e.set('parent_id', ie.parent_id)
268
def _pack_revision(self, rev):
269
"""Revision object -> xml tree"""
270
root = Element('revision',
271
committer = rev.committer,
272
timestamp = '%.9f' % rev.timestamp,
273
revision_id = rev.revision_id,
274
inventory_id = rev.inventory_id,
275
inventory_sha1 = rev.inventory_sha1,
278
root.set('timezone', str(rev.timezone))
281
msg = SubElement(root, 'message')
282
msg.text = rev.message
286
pelts = SubElement(root, 'parents')
287
pelts.tail = pelts.text = '\n'
288
for rr in rev.parents:
289
assert isinstance(rr, RevisionReference)
290
p = SubElement(pelts, 'revision_ref')
292
assert rr.revision_id
293
p.set('revision_id', rr.revision_id)
299
def _unpack_inventory(self, elt):
300
"""Construct from XML Element
302
assert elt.tag == 'inventory'
303
root_id = elt.get('file_id') or ROOT_ID
304
inv = Inventory(root_id)
306
ie = self._unpack_entry(e)
307
if ie.parent_id == ROOT_ID:
308
ie.parent_id = root_id
313
def _unpack_entry(self, elt):
315
assert kind == 'directory' or kind == 'file'
317
parent_id = elt.get('parent_id')
318
if parent_id == None:
321
ie = InventoryEntry(elt.get('file_id'),
325
ie.text_version = elt.get('text_version')
326
ie.entry_version = elt.get('entry_version')
327
ie.text_sha1 = elt.get('text_sha1')
328
v = elt.get('text_size')
329
ie.text_size = v and int(v)
334
def _unpack_revision(self, elt):
335
"""XML Element -> Revision object"""
336
assert elt.tag == 'revision'
338
rev = Revision(committer = elt.get('committer'),
339
timestamp = float(elt.get('timestamp')),
340
revision_id = elt.get('revision_id'),
341
inventory_id = elt.get('inventory_id'),
342
inventory_sha1 = elt.get('inventory_sha1')
345
for p in elt.find('parents'):
346
assert p.tag == 'revision_ref', \
347
"bad parent node tag %r" % p.tag
348
rev_ref = RevisionReference(p.get('revision_id'))
349
rev.parents.append(rev_ref)
351
v = elt.get('timezone')
352
rev.timezone = v and int(v)
354
rev.message = elt.findtext('message') # text of <message>
359
"""singleton instance"""
360
serializer_v4 = _Serializer_v4()
362
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))