2
# -*- coding: UTF-8 -*-
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
"""XML externalization support."""
20
# "XML is like violence: if it doesn't solve your problem, you aren't
21
# using enough of it." -- various
23
# importing this module is fairly slow because it has to load several
27
from util.cElementTree import ElementTree, SubElement, Element
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', 'symlink_target']:
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')
136
ie.symlink_target = elt.get('symlink_target')
138
## mutter("read inventoryentry: %r" % (elt.attrib))
140
v = elt.get('text_size')
141
ie.text_size = v and int(v)
146
def _pack_revision(self, rev):
147
"""Revision object -> xml tree"""
148
root = Element('revision',
149
committer = rev.committer,
150
timestamp = '%.9f' % rev.timestamp,
151
revision_id = rev.revision_id,
152
inventory_id = rev.inventory_id,
153
inventory_sha1 = rev.inventory_sha1,
156
root.set('timezone', str(rev.timezone))
159
msg = SubElement(root, 'message')
160
msg.text = rev.message
164
pelts = SubElement(root, 'parents')
165
pelts.tail = pelts.text = '\n'
166
for rr in rev.parents:
167
assert isinstance(rr, RevisionReference)
168
p = SubElement(pelts, 'revision_ref')
170
assert rr.revision_id
171
p.set('revision_id', rr.revision_id)
173
p.set('revision_sha1', rr.revision_sha1)
178
def _unpack_revision(self, elt):
179
"""XML Element -> Revision object"""
181
# <changeset> is deprecated...
182
if elt.tag not in ('revision', 'changeset'):
183
raise BzrError("unexpected tag in revision file: %r" % elt)
185
rev = Revision(committer = elt.get('committer'),
186
timestamp = float(elt.get('timestamp')),
187
revision_id = elt.get('revision_id'),
188
inventory_id = elt.get('inventory_id'),
189
inventory_sha1 = elt.get('inventory_sha1')
192
precursor = elt.get('precursor')
193
precursor_sha1 = elt.get('precursor_sha1')
195
pelts = elt.find('parents')
199
assert p.tag == 'revision_ref', \
200
"bad parent node tag %r" % p.tag
201
rev_ref = RevisionReference(p.get('revision_id'),
202
p.get('revision_sha1'))
203
rev.parents.append(rev_ref)
207
prec_parent = rev.parents[0].revision_id
208
assert prec_parent == precursor
210
# revisions written prior to 0.0.5 have a single precursor
211
# give as an attribute
212
rev_ref = RevisionReference(precursor, precursor_sha1)
213
rev.parents.append(rev_ref)
215
v = elt.get('timezone')
216
rev.timezone = v and int(v)
218
rev.message = elt.findtext('message') # text of <message>
223
class _Serializer_v5(Serializer):
224
"""Version 5 serializer
226
Packs objects into XML and vice versa.
228
You should use the serialzer_v5 singleton."""
232
def _pack_inventory(self, inv):
233
"""Convert to XML Element"""
234
e = Element('inventory')
236
if inv.root.file_id not in (None, ROOT_ID):
237
e.set('file_id', inv.root.file_id)
238
for path, ie in inv.iter_entries():
239
e.append(self._pack_entry(ie))
243
def _pack_entry(self, ie):
244
"""Convert InventoryEntry to XML element"""
245
assert ie.kind == 'directory' or ie.kind == 'file'
247
e.set('name', ie.name)
248
e.set('file_id', ie.file_id)
250
if ie.text_size != None:
251
e.set('text_size', '%d' % ie.text_size)
253
for f in ['text_version', 'text_sha1', 'entry_version']:
258
# to be conservative, we don't externalize the root pointers
259
# for now, leaving them as null in the xml form. in a future
260
# version it will be implied by nested elements.
261
if ie.parent_id != ROOT_ID:
262
assert isinstance(ie.parent_id, basestring)
263
e.set('parent_id', ie.parent_id)
270
def _pack_revision(self, rev):
271
"""Revision object -> xml tree"""
272
root = Element('revision',
273
committer = rev.committer,
274
timestamp = '%.9f' % rev.timestamp,
275
revision_id = rev.revision_id,
276
inventory_id = rev.inventory_id,
277
inventory_sha1 = rev.inventory_sha1,
280
root.set('timezone', str(rev.timezone))
283
msg = SubElement(root, 'message')
284
msg.text = rev.message
288
pelts = SubElement(root, 'parents')
289
pelts.tail = pelts.text = '\n'
290
for rr in rev.parents:
291
assert isinstance(rr, RevisionReference)
292
p = SubElement(pelts, 'revision_ref')
294
assert rr.revision_id
295
p.set('revision_id', rr.revision_id)
301
def _unpack_inventory(self, elt):
302
"""Construct from XML Element
304
assert elt.tag == 'inventory'
305
root_id = elt.get('file_id') or ROOT_ID
306
inv = Inventory(root_id)
308
ie = self._unpack_entry(e)
309
if ie.parent_id == ROOT_ID:
310
ie.parent_id = root_id
315
def _unpack_entry(self, elt):
317
assert kind == 'directory' or kind == 'file'
319
parent_id = elt.get('parent_id')
320
if parent_id == None:
323
ie = InventoryEntry(elt.get('file_id'),
327
ie.text_version = elt.get('text_version')
328
ie.entry_version = elt.get('entry_version')
329
ie.text_sha1 = elt.get('text_sha1')
330
v = elt.get('text_size')
331
ie.text_size = v and int(v)
336
def _unpack_revision(self, elt):
337
"""XML Element -> Revision object"""
338
assert elt.tag == 'revision'
340
rev = Revision(committer = elt.get('committer'),
341
timestamp = float(elt.get('timestamp')),
342
revision_id = elt.get('revision_id'),
343
inventory_id = elt.get('inventory_id'),
344
inventory_sha1 = elt.get('inventory_sha1')
347
for p in elt.find('parents'):
348
assert p.tag == 'revision_ref', \
349
"bad parent node tag %r" % p.tag
350
rev_ref = RevisionReference(p.get('revision_id'))
351
rev.parents.append(rev_ref)
353
v = elt.get('timezone')
354
rev.timezone = v and int(v)
356
rev.message = elt.findtext('message') # text of <message>
361
"""singleton instance"""
362
serializer_v4 = _Serializer_v4()
364
serializer_v5 = _Serializer_v5()