138
138
_to_escaped_map.clear()
141
class Serializer_v5(Serializer):
142
"""Version 5 serializer
141
class Serializer_v8(Serializer):
142
"""This serialiser adds rich roots.
144
Packs objects into XML and vice versa.
144
Its revision format number matches its inventory number.
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.
153
154
supported_kinds = set(['file', 'directory', 'symlink'])
155
def write_inventory_to_string(self, inv):
156
"""Just call write_inventory with a StringIO and return the value"""
156
revision_format_num = None
158
def _check_revisions(self, inv):
159
"""Extension point for subclasses to check during serialisation.
161
:param inv: An inventory about to be serialised, to be checked.
162
:raises: AssertionError if an error has occured.
164
if inv.revision_id is None:
165
raise AssertionError()
166
if inv.root.revision is None:
167
raise AssertionError()
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)
173
def write_inventory_to_string(self, inv, working=False):
174
"""Just call write_inventory with a StringIO and return the value.
176
:param working: If True skip history data - text_sha1, text_size,
177
reference_revision, symlink_target.
157
179
sio = cStringIO.StringIO()
158
self.write_inventory(inv, sio)
180
self.write_inventory(inv, sio, working)
159
181
return sio.getvalue()
161
def write_inventory(self, inv, f):
183
def write_inventory(self, inv, f, working=False):
162
184
"""Write inventory to a file.
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
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.
167
193
_ensure_utf8_re()
194
self._check_revisions(inv)
169
196
append = output.append
170
197
self._append_inventory_root(append, inv)
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)
208
if ie.kind == 'file':
210
executable = ' executable="yes"'
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,
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':
226
append('<directory file_id="%s name="%s%s%s revision="%s '
228
_encode_and_escape(ie.file_id),
229
_encode_and_escape(ie.name),
230
parent_str, parent_id,
231
_encode_and_escape(ie.revision)))
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':
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)))
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)
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)))
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))
268
raise errors.UnsupportedInventoryKind(ie.kind)
176
269
append('</inventory>\n')
178
272
# Just to keep the cache from growing without bounds
179
273
# but we may actually not want to do clear the cache
182
277
def _append_inventory_root(self, append, inv):
183
278
"""Append the inventory root to output."""
185
if inv.root.file_id not in (None, ROOT_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))
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)
203
append(' executable="yes"')
205
append(_encode_and_escape(ie.file_id))
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="')
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))
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)
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)))
233
292
def _pack_revision(self, rev):
234
293
"""Revision object -> xml tree"""
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'
281
def _unpack_inventory(self, elt):
282
"""Construct from XML Element
284
assert elt.tag == 'inventory'
285
root_id = elt.get('file_id') or ROOT_ID
286
root_id = _get_utf8_or_ascii(root_id)
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:
291
raise BzrError("invalid format version %r on inventory"
345
if format != self.format_num:
346
raise errors.UnexpectedInventoryFormat('Invalid format version %r'
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)
298
353
ie = self._unpack_entry(e)
299
if ie.parent_id is None:
300
ie.parent_id = root_id