1
# -*- coding: UTF-8 -*-
 
 
3
# This program is free software; you can redistribute it and/or modify
 
 
4
# it under the terms of the GNU General Public License as published by
 
 
5
# the Free Software Foundation; either version 2 of the License, or
 
 
6
# (at your option) any later version.
 
 
8
# This program is distributed in the hope that it will be useful,
 
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
 
11
# GNU General Public License for more details.
 
 
13
# You should have received a copy of the GNU General Public License
 
 
14
# along with this program; if not, write to the Free Software
 
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 
17
"""XML externalization support."""
 
 
19
# "XML is like violence: if it doesn't solve your problem, you aren't
 
 
20
# using enough of it." -- various
 
 
22
# importing this module is fairly slow because it has to load several
 
 
26
    from util.cElementTree import ElementTree, SubElement, Element
 
 
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()