~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/xml8.py

  • Committer: John Arbash Meinel
  • Date: 2008-09-26 22:14:42 UTC
  • mto: This revision was merged to the branch mainline in revision 3747.
  • Revision ID: john@arbash-meinel.com-20080926221442-3r67j99sr9rwe9w0
Make message optional, don't check the memory flag directly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
138
138
    _to_escaped_map.clear()
139
139
 
140
140
 
141
 
class Serializer_v5(Serializer):
142
 
    """Version 5 serializer
 
141
class Serializer_v8(Serializer):
 
142
    """This serialiser adds rich roots.
143
143
 
144
 
    Packs objects into XML and vice versa.
 
144
    Its revision format number matches its inventory number.
145
145
    """
146
 
    
 
146
 
147
147
    __slots__ = []
148
148
 
 
149
    root_id = None
149
150
    support_altered_by_hack = True
150
151
    # This format supports the altered-by hack that reads file ids directly out
151
152
    # of the versionedfile, without doing XML parsing.
152
153
 
153
154
    supported_kinds = set(['file', 'directory', 'symlink'])
154
 
 
155
 
    def write_inventory_to_string(self, inv):
156
 
        """Just call write_inventory with a StringIO and return the value"""
 
155
    format_num = '8'
 
156
    revision_format_num = None
 
157
 
 
158
    def _check_revisions(self, inv):
 
159
        """Extension point for subclasses to check during serialisation.
 
160
 
 
161
        :param inv: An inventory about to be serialised, to be checked.
 
162
        :raises: AssertionError if an error has occured.
 
163
        """
 
164
        if inv.revision_id is None:
 
165
            raise AssertionError()
 
166
        if inv.root.revision is None:
 
167
            raise AssertionError()
 
168
 
 
169
    def write_inventory_to_lines(self, inv):
 
170
        """Return a list of lines with the encoded inventory."""
 
171
        return self.write_inventory(inv, None)
 
172
 
 
173
    def write_inventory_to_string(self, inv, working=False):
 
174
        """Just call write_inventory with a StringIO and return the value.
 
175
 
 
176
        :param working: If True skip history data - text_sha1, text_size,
 
177
            reference_revision, symlink_target.
 
178
        """
157
179
        sio = cStringIO.StringIO()
158
 
        self.write_inventory(inv, sio)
 
180
        self.write_inventory(inv, sio, working)
159
181
        return sio.getvalue()
160
182
 
161
 
    def write_inventory(self, inv, f):
 
183
    def write_inventory(self, inv, f, working=False):
162
184
        """Write inventory to a file.
163
185
        
164
186
        :param inv: the inventory to write.
165
 
        :param f: the file to write.
 
187
        :param f: the file to write. (May be None if the lines are the desired
 
188
            output).
 
189
        :param working: If True skip history data - text_sha1, text_size,
 
190
            reference_revision, symlink_target.
 
191
        :return: The inventory as a list of lines.
166
192
        """
167
193
        _ensure_utf8_re()
 
194
        self._check_revisions(inv)
168
195
        output = []
169
196
        append = output.append
170
197
        self._append_inventory_root(append, inv)
172
199
        # Skip the root
173
200
        root_path, root_ie = entries.next()
174
201
        for path, ie in entries:
175
 
            self._append_entry(append, ie)
 
202
            if ie.parent_id != self.root_id:
 
203
                parent_str = ' parent_id="'
 
204
                parent_id  = _encode_and_escape(ie.parent_id)
 
205
            else:
 
206
                parent_str = ''
 
207
                parent_id  = ''
 
208
            if ie.kind == 'file':
 
209
                if ie.executable:
 
210
                    executable = ' executable="yes"'
 
211
                else:
 
212
                    executable = ''
 
213
                if not working:
 
214
                    append('<file%s file_id="%s name="%s%s%s revision="%s '
 
215
                        'text_sha1="%s" text_size="%d" />\n' % (
 
216
                        executable, _encode_and_escape(ie.file_id),
 
217
                        _encode_and_escape(ie.name), parent_str, parent_id,
 
218
                        _encode_and_escape(ie.revision), ie.text_sha1,
 
219
                        ie.text_size))
 
220
                else:
 
221
                    append('<file%s file_id="%s name="%s%s%s />\n' % (
 
222
                        executable, _encode_and_escape(ie.file_id),
 
223
                        _encode_and_escape(ie.name), parent_str, parent_id))
 
224
            elif ie.kind == 'directory':
 
225
                if not working:
 
226
                    append('<directory file_id="%s name="%s%s%s revision="%s '
 
227
                        '/>\n' % (
 
228
                        _encode_and_escape(ie.file_id),
 
229
                        _encode_and_escape(ie.name),
 
230
                        parent_str, parent_id,
 
231
                        _encode_and_escape(ie.revision)))
 
232
                else:
 
233
                    append('<directory file_id="%s name="%s%s%s />\n' % (
 
234
                        _encode_and_escape(ie.file_id),
 
235
                        _encode_and_escape(ie.name),
 
236
                        parent_str, parent_id))
 
237
            elif ie.kind == 'symlink':
 
238
                if not working:
 
239
                    append('<symlink file_id="%s name="%s%s%s revision="%s '
 
240
                        'symlink_target="%s />\n' % (
 
241
                        _encode_and_escape(ie.file_id),
 
242
                        _encode_and_escape(ie.name),
 
243
                        parent_str, parent_id,
 
244
                        _encode_and_escape(ie.revision),
 
245
                        _encode_and_escape(ie.symlink_target)))
 
246
                else:
 
247
                    append('<symlink file_id="%s name="%s%s%s />\n' % (
 
248
                        _encode_and_escape(ie.file_id),
 
249
                        _encode_and_escape(ie.name),
 
250
                        parent_str, parent_id))
 
251
            elif ie.kind == 'tree-reference':
 
252
                if ie.kind not in self.supported_kinds:
 
253
                    raise errors.UnsupportedInventoryKind(ie.kind)
 
254
                if not working:
 
255
                    append('<tree-reference file_id="%s name="%s%s%s '
 
256
                        'revision="%s reference_revision="%s />\n' % (
 
257
                        _encode_and_escape(ie.file_id),
 
258
                        _encode_and_escape(ie.name),
 
259
                        parent_str, parent_id,
 
260
                        _encode_and_escape(ie.revision),
 
261
                        _encode_and_escape(ie.reference_revision)))
 
262
                else:
 
263
                    append('<tree-reference file_id="%s name="%s%s%s />\n' % (
 
264
                        _encode_and_escape(ie.file_id),
 
265
                        _encode_and_escape(ie.name),
 
266
                        parent_str, parent_id))
 
267
            else:
 
268
                raise errors.UnsupportedInventoryKind(ie.kind)
176
269
        append('</inventory>\n')
177
 
        f.writelines(output)
 
270
        if f is not None:
 
271
            f.writelines(output)
178
272
        # Just to keep the cache from growing without bounds
179
273
        # but we may actually not want to do clear the cache
180
274
        #_clear_cache()
 
275
        return output
181
276
 
182
277
    def _append_inventory_root(self, append, inv):
183
278
        """Append the inventory root to output."""
184
 
        append('<inventory')
185
 
        if inv.root.file_id not in (None, ROOT_ID):
186
 
            append(' file_id="')
187
 
            append(_encode_and_escape(inv.root.file_id))
188
 
        append(' format="5"')
189
279
        if inv.revision_id is not None:
190
 
            append(' revision_id="')
191
 
            append(_encode_and_escape(inv.revision_id))
192
 
        append('>\n')
193
 
        
194
 
    def _append_entry(self, append, ie):
195
 
        """Convert InventoryEntry to XML element and append to output."""
196
 
        # TODO: should just be a plain assertion
197
 
        if ie.kind not in self.supported_kinds:
198
 
            raise errors.UnsupportedInventoryKind(ie.kind)
199
 
 
200
 
        append("<")
201
 
        append(ie.kind)
202
 
        if ie.executable:
203
 
            append(' executable="yes"')
204
 
        append(' file_id="')
205
 
        append(_encode_and_escape(ie.file_id))
206
 
        append(' name="')
207
 
        append(_encode_and_escape(ie.name))
208
 
        if self._parent_condition(ie):
209
 
            assert isinstance(ie.parent_id, basestring)
210
 
            append(' parent_id="')
211
 
            append(_encode_and_escape(ie.parent_id))
212
 
        if ie.revision is not None:
213
 
            append(' revision="')
214
 
            append(_encode_and_escape(ie.revision))
215
 
        if ie.symlink_target is not None:
216
 
            append(' symlink_target="')
217
 
            append(_encode_and_escape(ie.symlink_target))
218
 
        if ie.text_sha1 is not None:
219
 
            append(' text_sha1="')
220
 
            append(ie.text_sha1)
221
 
            append('"')
222
 
        if ie.text_size is not None:
223
 
            append(' text_size="%d"' % ie.text_size)
224
 
        if getattr(ie, 'reference_revision', None) is not None:
225
 
            append(' reference_revision="')
226
 
            append(_encode_and_escape(ie.reference_revision))
227
 
        append(" />\n")
228
 
        return
229
 
 
230
 
    def _parent_condition(self, ie):
231
 
        return ie.parent_id != ROOT_ID
 
280
            revid1 = ' revision_id="'
 
281
            revid2 = _encode_and_escape(inv.revision_id)
 
282
        else:
 
283
            revid1 = ""
 
284
            revid2 = ""
 
285
        append('<inventory format="%s"%s%s>\n' % (
 
286
            self.format_num, revid1, revid2))
 
287
        append('<directory file_id="%s name="%s revision="%s />\n' % (
 
288
            _encode_and_escape(inv.root.file_id),
 
289
            _encode_and_escape(inv.root.name),
 
290
            _encode_and_escape(inv.root.revision)))
232
291
 
233
292
    def _pack_revision(self, rev):
234
293
        """Revision object -> xml tree"""
239
298
        revision_id = rev.revision_id
240
299
        if isinstance(revision_id, str):
241
300
            revision_id = decode_utf8(revision_id)
 
301
        format_num = self.format_num
 
302
        if self.revision_format_num is not None:
 
303
            format_num = self.revision_format_num
242
304
        root = Element('revision',
243
305
                       committer = rev.committer,
244
306
                       timestamp = '%.3f' % rev.timestamp,
245
307
                       revision_id = revision_id,
246
308
                       inventory_sha1 = rev.inventory_sha1,
247
 
                       format='5',
 
309
                       format=format_num,
248
310
                       )
249
311
        if rev.timezone is not None:
250
312
            root.set('timezone', str(rev.timezone))
256
318
            pelts = SubElement(root, 'parents')
257
319
            pelts.tail = pelts.text = '\n'
258
320
            for parent_id in rev.parent_ids:
259
 
                assert isinstance(parent_id, basestring)
260
321
                _mod_revision.check_not_reserved_id(parent_id)
261
322
                p = SubElement(pelts, 'revision_ref')
262
323
                p.tail = '\n'
270
331
    def _pack_revision_properties(self, rev, under_element):
271
332
        top_elt = SubElement(under_element, 'properties')
272
333
        for prop_name, prop_value in sorted(rev.properties.items()):
273
 
            assert isinstance(prop_name, basestring) 
274
 
            assert isinstance(prop_value, basestring) 
275
334
            prop_elt = SubElement(top_elt, 'property')
276
335
            prop_elt.set('name', prop_name)
277
336
            prop_elt.text = prop_value
278
337
            prop_elt.tail = '\n'
279
338
        top_elt.tail = '\n'
280
339
 
281
 
    def _unpack_inventory(self, elt):
282
 
        """Construct from XML Element
283
 
        """
284
 
        assert elt.tag == 'inventory'
285
 
        root_id = elt.get('file_id') or ROOT_ID
286
 
        root_id = _get_utf8_or_ascii(root_id)
287
 
 
 
340
    def _unpack_inventory(self, elt, revision_id=None):
 
341
        """Construct from XML Element"""
 
342
        if elt.tag != 'inventory':
 
343
            raise errors.UnexpectedInventoryFormat('Root tag is %r' % elt.tag)
288
344
        format = elt.get('format')
289
 
        if format is not None:
290
 
            if format != '5':
291
 
                raise BzrError("invalid format version %r on inventory"
292
 
                                % format)
 
345
        if format != self.format_num:
 
346
            raise errors.UnexpectedInventoryFormat('Invalid format version %r'
 
347
                                                   % format)
293
348
        revision_id = elt.get('revision_id')
294
349
        if revision_id is not None:
295
350
            revision_id = cache_utf8.encode(revision_id)
296
 
        inv = Inventory(root_id, revision_id=revision_id)
 
351
        inv = inventory.Inventory(root_id=None, revision_id=revision_id)
297
352
        for e in elt:
298
353
            ie = self._unpack_entry(e)
299
 
            if ie.parent_id is None:
300
 
                ie.parent_id = root_id
301
354
            inv.add(ie)
302
355
        return inv
303
356
 
342
395
 
343
396
    def _unpack_revision(self, elt):
344
397
        """XML Element -> Revision object"""
345
 
        assert elt.tag == 'revision'
346
398
        format = elt.get('format')
 
399
        format_num = self.format_num
 
400
        if self.revision_format_num is not None:
 
401
            format_num = self.revision_format_num
347
402
        if format is not None:
348
 
            if format != '5':
349
 
                raise BzrError("invalid format version %r on inventory"
 
403
            if format != format_num:
 
404
                raise BzrError("invalid format version %r on revision"
350
405
                                % format)
351
406
        get_cached = _get_utf8_or_ascii
352
407
        rev = Revision(committer = elt.get('committer'),
356
411
                       )
357
412
        parents = elt.find('parents') or []
358
413
        for p in parents:
359
 
            assert p.tag == 'revision_ref', \
360
 
                   "bad parent node tag %r" % p.tag
361
414
            rev.parent_ids.append(get_cached(p.get('revision_id')))
362
415
        self._unpack_revision_properties(elt, rev)
363
416
        v = elt.get('timezone')
371
424
    def _unpack_revision_properties(self, elt, rev):
372
425
        """Unpack properties onto a revision."""
373
426
        props_elt = elt.find('properties')
374
 
        assert len(rev.properties) == 0
375
427
        if not props_elt:
376
428
            return
377
429
        for prop_elt in props_elt:
378
 
            assert prop_elt.tag == 'property', \
379
 
                "bad tag under properties list: %r" % prop_elt.tag
 
430
            if prop_elt.tag != 'property':
 
431
                raise AssertionError(
 
432
                    "bad tag under properties list: %r" % prop_elt.tag)
380
433
            name = prop_elt.get('name')
381
434
            value = prop_elt.text
382
435
            # If a property had an empty value ('') cElementTree reads
384
437
            # properties have string values
385
438
            if value is None:
386
439
                value = ''
387
 
            assert name not in rev.properties, \
388
 
                "repeated property %r" % name
 
440
            if name in rev.properties:
 
441
                raise AssertionError("repeated property %r" % name)
389
442
            rev.properties[name] = value
390
443
 
391
444
 
392
 
serializer_v5 = Serializer_v5()
 
445
serializer_v8 = Serializer_v8()