~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/xml.py

  • Committer: Robert Collins
  • Date: 2005-09-28 05:25:54 UTC
  • mfrom: (1185.1.42)
  • mto: (1092.2.18)
  • mto: This revision was merged to the branch mainline in revision 1397.
  • Revision ID: robertc@robertcollins.net-20050928052554-beb985505f77ea6a
update symlink branch to integration

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#! /usr/bin/env python
1
2
# -*- coding: UTF-8 -*-
2
3
 
3
4
# This program is free software; you can redistribute it and/or modify
22
23
# importing this module is fairly slow because it has to load several
23
24
# ElementTree bits
24
25
 
25
 
from bzrlib.trace import mutter, warning
26
 
 
27
26
try:
28
 
    from cElementTree import (ElementTree, SubElement, Element,
29
 
                              XMLTreeBuilder, fromstring, tostring)
 
27
    from util.cElementTree import ElementTree, SubElement, Element
30
28
except ImportError:
31
 
    mutter('WARNING: using slower ElementTree; consider installing cElementTree'
32
 
           " and make sure it's on your PYTHONPATH")
33
 
    from util.elementtree.ElementTree import (ElementTree, SubElement,
34
 
                                              Element, XMLTreeBuilder,
35
 
                                              fromstring, tostring)
 
29
    from util.elementtree.ElementTree import ElementTree, SubElement, Element
36
30
 
 
31
from bzrlib.inventory import ROOT_ID, Inventory, InventoryEntry
 
32
from bzrlib.revision import Revision, RevisionReference        
37
33
from bzrlib.errors import BzrError
38
34
 
39
35
 
44
40
        elt = self._pack_inventory(inv)
45
41
        self._write_element(elt, f)
46
42
 
47
 
    def write_inventory_to_string(self, inv):
48
 
        return tostring(self._pack_inventory(inv)) + '\n'
49
 
 
50
 
    def read_inventory_from_string(self, xml_string):
51
 
        return self._unpack_inventory(fromstring(xml_string))
52
 
 
53
43
    def read_inventory(self, f):
54
44
        return self._unpack_inventory(self._read_element(f))
55
45
 
56
46
    def write_revision(self, rev, f):
57
47
        self._write_element(self._pack_revision(rev), f)
58
48
 
59
 
    def write_revision_to_string(self, rev):
60
 
        return tostring(self._pack_revision(rev)) + '\n'
61
 
 
62
49
    def read_revision(self, f):
63
50
        return self._unpack_revision(self._read_element(f))
64
51
 
65
 
    def read_revision_from_string(self, xml_string):
66
 
        return self._unpack_revision(fromstring(xml_string))
67
 
 
68
52
    def _write_element(self, elt, f):
69
53
        ElementTree(elt).write(f, 'utf-8')
70
54
        f.write('\n')
71
55
 
72
56
    def _read_element(self, f):
73
57
        return ElementTree().parse(f)
 
58
 
 
59
 
 
60
 
 
61
class _Serializer_v4(Serializer):
 
62
    """Version 0.0.4 serializer
 
63
 
 
64
    You should use the serialzer_v4 singleton."""
 
65
    
 
66
    __slots__ = []
 
67
    
 
68
    def _pack_inventory(self, inv):
 
69
        """Convert to XML Element"""
 
70
        e = Element('inventory')
 
71
        e.text = '\n'
 
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))
 
76
        return e
 
77
 
 
78
 
 
79
    def _pack_entry(self, ie):
 
80
        """Convert InventoryEntry to XML element"""
 
81
        e = Element('entry')
 
82
        e.set('name', ie.name)
 
83
        e.set('file_id', ie.file_id)
 
84
        e.set('kind', ie.kind)
 
85
 
 
86
        if ie.text_size != None:
 
87
            e.set('text_size', '%d' % ie.text_size)
 
88
 
 
89
        for f in ['text_id', 'text_sha1', 'symlink_target']:
 
90
            v = getattr(ie, f)
 
91
            if v != None:
 
92
                e.set(f, v)
 
93
 
 
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)
 
100
 
 
101
        e.tail = '\n'
 
102
 
 
103
        return e
 
104
 
 
105
 
 
106
    def _unpack_inventory(self, elt):
 
107
        """Construct from XML Element
 
108
        """
 
109
        assert elt.tag == 'inventory'
 
110
        root_id = elt.get('file_id') or ROOT_ID
 
111
        inv = Inventory(root_id)
 
112
        for e in elt:
 
113
            ie = self._unpack_entry(e)
 
114
            if ie.parent_id == ROOT_ID:
 
115
                ie.parent_id = root_id
 
116
            inv.add(ie)
 
117
        return inv
 
118
 
 
119
 
 
120
    def _unpack_entry(self, elt):
 
121
        assert elt.tag == 'entry'
 
122
 
 
123
        ## original format inventories don't have a parent_id for
 
124
        ## nodes in the root directory, but it's cleaner to use one
 
125
        ## internally.
 
126
        parent_id = elt.get('parent_id')
 
127
        if parent_id == None:
 
128
            parent_id = ROOT_ID
 
129
 
 
130
        ie = InventoryEntry(elt.get('file_id'),
 
131
                              elt.get('name'),
 
132
                              elt.get('kind'),
 
133
                              parent_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')
 
137
 
 
138
        ## mutter("read inventoryentry: %r" % (elt.attrib))
 
139
 
 
140
        v = elt.get('text_size')
 
141
        ie.text_size = v and int(v)
 
142
 
 
143
        return ie
 
144
 
 
145
 
 
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,
 
154
                       )
 
155
        if rev.timezone:
 
156
            root.set('timezone', str(rev.timezone))
 
157
        root.text = '\n'
 
158
 
 
159
        msg = SubElement(root, 'message')
 
160
        msg.text = rev.message
 
161
        msg.tail = '\n'
 
162
 
 
163
        if rev.parents:
 
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')
 
169
                p.tail = '\n'
 
170
                assert rr.revision_id
 
171
                p.set('revision_id', rr.revision_id)
 
172
                if rr.revision_sha1:
 
173
                    p.set('revision_sha1', rr.revision_sha1)
 
174
 
 
175
        return root
 
176
 
 
177
    
 
178
    def _unpack_revision(self, elt):
 
179
        """XML Element -> Revision object"""
 
180
        
 
181
        # <changeset> is deprecated...
 
182
        if elt.tag not in ('revision', 'changeset'):
 
183
            raise BzrError("unexpected tag in revision file: %r" % elt)
 
184
 
 
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')
 
190
                       )
 
191
 
 
192
        precursor = elt.get('precursor')
 
193
        precursor_sha1 = elt.get('precursor_sha1')
 
194
 
 
195
        pelts = elt.find('parents')
 
196
 
 
197
        if pelts:
 
198
            for p in pelts:
 
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)
 
204
 
 
205
            if precursor:
 
206
                # must be consistent
 
207
                prec_parent = rev.parents[0].revision_id
 
208
                assert prec_parent == precursor
 
209
        elif 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)
 
214
 
 
215
        v = elt.get('timezone')
 
216
        rev.timezone = v and int(v)
 
217
 
 
218
        rev.message = elt.findtext('message') # text of <message>
 
219
        return rev
 
220
 
 
221
 
 
222
 
 
223
class _Serializer_v5(Serializer):
 
224
    """Version 5 serializer
 
225
 
 
226
    Packs objects into XML and vice versa.
 
227
 
 
228
    You should use the serialzer_v5 singleton."""
 
229
    
 
230
    __slots__ = []
 
231
    
 
232
    def _pack_inventory(self, inv):
 
233
        """Convert to XML Element"""
 
234
        e = Element('inventory')
 
235
        e.text = '\n'
 
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))
 
240
        return e
 
241
 
 
242
 
 
243
    def _pack_entry(self, ie):
 
244
        """Convert InventoryEntry to XML element"""
 
245
        assert ie.kind == 'directory' or ie.kind == 'file'
 
246
        e = Element(ie.kind)
 
247
        e.set('name', ie.name)
 
248
        e.set('file_id', ie.file_id)
 
249
 
 
250
        if ie.text_size != None:
 
251
            e.set('text_size', '%d' % ie.text_size)
 
252
 
 
253
        for f in ['text_version', 'text_sha1', 'entry_version']:
 
254
            v = getattr(ie, f)
 
255
            if v != None:
 
256
                e.set(f, v)
 
257
 
 
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)
 
264
 
 
265
        e.tail = '\n'
 
266
 
 
267
        return e
 
268
 
 
269
 
 
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,
 
278
                       )
 
279
        if rev.timezone:
 
280
            root.set('timezone', str(rev.timezone))
 
281
        root.text = '\n'
 
282
 
 
283
        msg = SubElement(root, 'message')
 
284
        msg.text = rev.message
 
285
        msg.tail = '\n'
 
286
 
 
287
        if rev.parents:
 
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')
 
293
                p.tail = '\n'
 
294
                assert rr.revision_id
 
295
                p.set('revision_id', rr.revision_id)
 
296
 
 
297
        return root
 
298
 
 
299
    
 
300
 
 
301
    def _unpack_inventory(self, elt):
 
302
        """Construct from XML Element
 
303
        """
 
304
        assert elt.tag == 'inventory'
 
305
        root_id = elt.get('file_id') or ROOT_ID
 
306
        inv = Inventory(root_id)
 
307
        for e in elt:
 
308
            ie = self._unpack_entry(e)
 
309
            if ie.parent_id == ROOT_ID:
 
310
                ie.parent_id = root_id
 
311
            inv.add(ie)
 
312
        return inv
 
313
 
 
314
 
 
315
    def _unpack_entry(self, elt):
 
316
        kind = elt.tag
 
317
        assert kind == 'directory' or kind == 'file'
 
318
 
 
319
        parent_id = elt.get('parent_id')
 
320
        if parent_id == None:
 
321
            parent_id = ROOT_ID
 
322
 
 
323
        ie = InventoryEntry(elt.get('file_id'),
 
324
                            elt.get('name'),
 
325
                            kind,
 
326
                            parent_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)
 
332
 
 
333
        return ie
 
334
 
 
335
 
 
336
    def _unpack_revision(self, elt):
 
337
        """XML Element -> Revision object"""
 
338
        assert elt.tag == 'revision'
 
339
        
 
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')
 
345
                       )
 
346
 
 
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)
 
352
 
 
353
        v = elt.get('timezone')
 
354
        rev.timezone = v and int(v)
 
355
 
 
356
        rev.message = elt.findtext('message') # text of <message>
 
357
        return rev
 
358
 
 
359
 
 
360
 
 
361
"""singleton instance"""
 
362
serializer_v4 = _Serializer_v4()
 
363
 
 
364
serializer_v5 = _Serializer_v5()