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
24
__copyright__ = "Copyright (C) 2005 Canonical Ltd."
25
__author__ = "Martin Pool <mbp@canonical.com>"
26
from util.cElementTree import (ElementTree, SubElement, Element,
28
from cElementTree import Element, ElementTree, SubElement
28
29
except ImportError:
29
from util.elementtree.ElementTree import (ElementTree, SubElement,
30
Element, XMLTreeBuilder)
32
from bzrlib.inventory import ROOT_ID, Inventory, InventoryEntry
33
from bzrlib.revision import Revision, RevisionReference
34
from bzrlib.errors import BzrError
37
class Serializer(object):
38
"""Abstract object serialize/deserialize"""
39
def write_inventory(self, inv, f):
40
"""Write inventory to a file"""
41
elt = self._pack_inventory(inv)
42
self._write_element(elt, f)
44
def read_inventory_from_string(self, xml_string):
45
return self._unpack_inventory(self._parse_string(xml_string))
47
def read_inventory(self, f):
48
return self._unpack_inventory(self._read_element(f))
50
def write_revision(self, rev, f):
51
self._write_element(self._pack_revision(rev), f)
53
def read_revision(self, f):
54
return self._unpack_revision(self._read_element(f))
56
def read_revision_from_string(self, xml_string):
57
return self._unpack_revision(self._parse_string(xml_string))
59
def _write_element(self, elt, f):
60
ElementTree(elt).write(f, 'utf-8')
30
from elementtree.ElementTree import Element, ElementTree, SubElement
33
from trace import mutter
37
raise Exception("XMLMixin.to_element must be overridden in concrete classes")
39
def write_xml(self, f):
40
ElementTree(self.to_element()).write(f, 'utf-8')
63
def _read_element(self, f):
64
return ElementTree().parse(f)
66
def _parse_string(self, xml_string):
67
parser = XMLTreeBuilder()
68
parser.feed(xml_string)
73
class _Serializer_v4(Serializer):
74
"""Version 0.0.4 serializer
76
You should use the serialzer_v4 singleton."""
80
def _pack_inventory(self, inv):
81
"""Convert to XML Element"""
82
e = Element('inventory')
84
if inv.root.file_id not in (None, ROOT_ID):
85
e.set('file_id', inv.root.file_id)
86
for path, ie in inv.iter_entries():
87
e.append(self._pack_entry(ie))
91
def _pack_entry(self, ie):
92
"""Convert InventoryEntry to XML element"""
94
e.set('name', ie.name)
95
e.set('file_id', ie.file_id)
96
e.set('kind', ie.kind)
98
if ie.text_size != None:
99
e.set('text_size', '%d' % ie.text_size)
101
for f in ['text_id', 'text_sha1']:
106
# to be conservative, we don't externalize the root pointers
107
# for now, leaving them as null in the xml form. in a future
108
# version it will be implied by nested elements.
109
if ie.parent_id != ROOT_ID:
110
assert isinstance(ie.parent_id, basestring)
111
e.set('parent_id', ie.parent_id)
118
def _unpack_inventory(self, elt):
119
"""Construct from XML Element
121
assert elt.tag == 'inventory'
122
root_id = elt.get('file_id') or ROOT_ID
123
inv = Inventory(root_id)
125
ie = self._unpack_entry(e)
126
if ie.parent_id == ROOT_ID:
127
ie.parent_id = root_id
132
def _unpack_entry(self, elt):
133
assert elt.tag == 'entry'
135
## original format inventories don't have a parent_id for
136
## nodes in the root directory, but it's cleaner to use one
138
parent_id = elt.get('parent_id')
139
if parent_id == None:
142
ie = InventoryEntry(elt.get('file_id'),
146
ie.text_id = elt.get('text_id')
147
ie.text_sha1 = elt.get('text_sha1')
149
## mutter("read inventoryentry: %r" % (elt.attrib))
151
v = elt.get('text_size')
152
ie.text_size = v and int(v)
157
def _pack_revision(self, rev):
158
"""Revision object -> xml tree"""
159
root = Element('revision',
160
committer = rev.committer,
161
timestamp = '%.9f' % rev.timestamp,
162
revision_id = rev.revision_id,
163
inventory_id = rev.inventory_id,
164
inventory_sha1 = rev.inventory_sha1,
167
root.set('timezone', str(rev.timezone))
170
msg = SubElement(root, 'message')
171
msg.text = rev.message
175
pelts = SubElement(root, 'parents')
176
pelts.tail = pelts.text = '\n'
177
for rr in rev.parents:
178
assert isinstance(rr, RevisionReference)
179
p = SubElement(pelts, 'revision_ref')
181
assert rr.revision_id
182
p.set('revision_id', rr.revision_id)
184
p.set('revision_sha1', rr.revision_sha1)
189
def _unpack_revision(self, elt):
190
"""XML Element -> Revision object"""
192
# <changeset> is deprecated...
193
if elt.tag not in ('revision', 'changeset'):
194
raise BzrError("unexpected tag in revision file: %r" % elt)
196
rev = Revision(committer = elt.get('committer'),
197
timestamp = float(elt.get('timestamp')),
198
revision_id = elt.get('revision_id'),
199
inventory_id = elt.get('inventory_id'),
200
inventory_sha1 = elt.get('inventory_sha1')
203
precursor = elt.get('precursor')
204
precursor_sha1 = elt.get('precursor_sha1')
206
pelts = elt.find('parents')
210
assert p.tag == 'revision_ref', \
211
"bad parent node tag %r" % p.tag
212
rev_ref = RevisionReference(p.get('revision_id'),
213
p.get('revision_sha1'))
214
rev.parents.append(rev_ref)
218
prec_parent = rev.parents[0].revision_id
219
assert prec_parent == precursor
221
# revisions written prior to 0.0.5 have a single precursor
222
# give as an attribute
223
rev_ref = RevisionReference(precursor, precursor_sha1)
224
rev.parents.append(rev_ref)
226
v = elt.get('timezone')
227
rev.timezone = v and int(v)
229
rev.message = elt.findtext('message') # text of <message>
235
"""singleton instance"""
236
serializer_v4 = _Serializer_v4()
44
return cls.from_element(ElementTree().parse(f))
46
read_xml = classmethod(read_xml)