/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
3586.1.2 by Ian Clatworthy
first cut of views.py
1
# Copyright (C) 2008 Canonical Ltd
2
#
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.
7
#
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.
12
#
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
16
17
"""View management.
18
3586.1.33 by Ian Clatworthy
cleanup trailing whitespace
19
Views are contained within a working tree and normally constructed
3586.1.2 by Ian Clatworthy
first cut of views.py
20
when first accessed.  Clients should do, for example, ...
21
22
  tree.views.lookup_view()
23
"""
24
25
26
import re
27
28
from bzrlib import (
29
    errors,
30
    )
31
32
33
_VIEWS_FORMAT_MARKER_RE = re.compile(r'Bazaar views format (\d+)')
3586.2.4 by Ian Clatworthy
fix storage after deleting the last view
34
_VIEWS_FORMAT1_MARKER = "Bazaar views format 1\n"
3586.1.2 by Ian Clatworthy
first cut of views.py
35
36
37
class _Views(object):
38
    """Base class for View managers."""
39
40
    def supports_views(self):
41
        raise NotImplementedError(self.supports_views)
42
43
44
class PathBasedViews(_Views):
45
    """View storage in an unversioned tree control file.
46
47
    Views are stored in terms of paths relative to the tree root.
48
49
    The top line of the control file is a format marker in the format:
50
51
      Bazaar views format X
52
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
53
    where X is an integer number. After this top line, version 1 format is
54
    stored as follows:
55
56
     * optional name-values pairs in the format 'name=value'
57
58
     * optional view definitions, one per line in the format
59
60
       views:
61
       name file1 file2 ...
3586.1.2 by Ian Clatworthy
first cut of views.py
62
       name file1 file2 ...
63
64
    where the fields are separated by a nul character (\0). The views file
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
65
    is encoded in utf-8. The only supported keyword in version 1 is
66
    'current' which stores the name of the current view, if any.
3586.1.2 by Ian Clatworthy
first cut of views.py
67
    """
68
69
    def __init__(self, tree):
3586.1.7 by Ian Clatworthy
first cut at WTF5
70
        self.tree = tree
3586.1.2 by Ian Clatworthy
first cut of views.py
71
        self._loaded = False
72
        self._current = None
73
        self._views = {}
74
75
    def supports_views(self):
76
        return True
77
78
    def get_view_info(self):
79
        """Get the current view and dictionary of views.
80
81
        :return: current, views where
82
          current = the name of the current view or None if no view is enabled
83
          views = a map from view name to list of files/directories
84
        """
85
        self._load_view_info()
86
        return self._current, self._views
87
88
    def set_view_info(self, current, views):
89
        """Set the current view and dictionary of views.
90
91
        :param current: the name of the current view or None if no view is
92
          enabled
93
        :param views: a map from view name to list of files/directories
94
        """
95
        if current is not None and current not in views:
96
            raise errors.NoSuchView(current)
97
        self.tree.lock_write()
98
        try:
99
            self._current = current
100
            self._views = views
101
            self._save_view_info()
102
        finally:
103
            self.tree.unlock()
104
105
    def lookup_view(self, view_name=None):
106
        """Return the contents of a view.
4032.1.2 by John Arbash Meinel
Track down a few more files that have trailing whitespace.
107
3586.1.2 by Ian Clatworthy
first cut of views.py
108
        :param view_Name: name of the view or None to lookup the current view
109
        :return: the list of files/directories in the requested view
110
        """
111
        self._load_view_info()
112
        try:
113
            if view_name is None:
114
                if self._current:
115
                    view_name = self._current
116
                else:
117
                    return []
118
            return self._views[view_name]
119
        except KeyError:
120
            raise errors.NoSuchView(view_name)
121
122
    def set_view(self, view_name, view_files, make_current=True):
123
        """Add or update a view definition.
4032.1.2 by John Arbash Meinel
Track down a few more files that have trailing whitespace.
124
3586.1.2 by Ian Clatworthy
first cut of views.py
125
        :param view_name: the name of the view
126
        :param view_files: the list of files/directories in the view
127
        :param make_current: make this view the current one or not
128
        """
129
        self.tree.lock_write()
130
        try:
131
            self._load_view_info()
132
            self._views[view_name] = view_files
133
            if make_current:
134
                self._current = view_name
135
            self._save_view_info()
136
        finally:
137
            self.tree.unlock()
138
139
    def delete_view(self, view_name):
140
        """Delete a view definition.
141
142
        If the view deleted is the current one, the current view is reset.
143
        """
144
        self.tree.lock_write()
145
        try:
146
            self._load_view_info()
147
            try:
148
                del self._views[view_name]
149
            except KeyError:
150
                raise errors.NoSuchView(view_name)
3586.1.7 by Ian Clatworthy
first cut at WTF5
151
            if view_name == self._current:
3586.1.2 by Ian Clatworthy
first cut of views.py
152
                self._current = None
153
            self._save_view_info()
154
        finally:
155
            self.tree.unlock()
156
157
    def _save_view_info(self):
158
        """Save the current view and all view definitions.
159
160
        Be sure to have initialised self._current and self._views before
161
        calling this method.
162
        """
163
        self.tree.lock_write()
164
        try:
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
165
            if self._current is None:
166
                keywords = {}
167
            else:
168
                keywords = {'current': self._current}
3586.1.2 by Ian Clatworthy
first cut of views.py
169
            self.tree._transport.put_bytes('views',
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
170
                self._serialize_view_content(keywords, self._views))
3586.1.2 by Ian Clatworthy
first cut of views.py
171
        finally:
172
            self.tree.unlock()
173
174
    def _load_view_info(self):
175
        """Load the current view and dictionary of view definitions."""
176
        if not self._loaded:
177
            self.tree.lock_read()
178
            try:
179
                try:
180
                    view_content = self.tree._transport.get_bytes('views')
181
                except errors.NoSuchFile, e:
182
                    self._current, self._views = None, {}
183
                else:
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
184
                    keywords, self._views = \
3586.1.2 by Ian Clatworthy
first cut of views.py
185
                        self._deserialize_view_content(view_content)
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
186
                    self._current = keywords.get('current')
3586.1.2 by Ian Clatworthy
first cut of views.py
187
            finally:
188
                self.tree.unlock()
189
            self._loaded = True
190
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
191
    def _serialize_view_content(self, keywords, view_dict):
192
        """Convert view keywords and a view dictionary into a stream."""
3586.1.7 by Ian Clatworthy
first cut at WTF5
193
        lines = [_VIEWS_FORMAT1_MARKER]
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
194
        for key in keywords:
195
            line = "%s=%s\n" % (key,keywords[key])
196
            lines.append(line.encode('utf-8'))
197
        if view_dict:
198
            lines.append("views:\n".encode('utf-8'))
199
            for view in sorted(view_dict):
200
                view_data = "%s\0%s\n" % (view, "\0".join(view_dict[view]))
201
                lines.append(view_data.encode('utf-8'))
3586.2.4 by Ian Clatworthy
fix storage after deleting the last view
202
        return "".join(lines)
3586.1.2 by Ian Clatworthy
first cut of views.py
203
204
    def _deserialize_view_content(self, view_content):
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
205
        """Convert a stream into view keywords and a dictionary of views."""
3586.1.2 by Ian Clatworthy
first cut of views.py
206
        # as a special case to make initialization easy, an empty definition
207
        # maps to no current view and an empty view dictionary
208
        if view_content == '':
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
209
            return {}, {}
3586.1.2 by Ian Clatworthy
first cut of views.py
210
        lines = view_content.splitlines()
3586.1.7 by Ian Clatworthy
first cut at WTF5
211
        match = _VIEWS_FORMAT_MARKER_RE.match(lines[0])
3586.1.2 by Ian Clatworthy
first cut of views.py
212
        if not match:
213
            raise ValueError(
214
                "format marker missing from top of views file")
3586.1.7 by Ian Clatworthy
first cut at WTF5
215
        elif match.group(1) != '1':
3586.1.2 by Ian Clatworthy
first cut of views.py
216
            raise ValueError(
217
                "cannot decode views format %s" % match.group(1))
218
        try:
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
219
            keywords = {}
3586.1.2 by Ian Clatworthy
first cut of views.py
220
            views = {}
3586.2.11 by Ian Clatworthy
use name-values pairs instead of positions in views file
221
            in_views = False
222
            for line in lines[1:]:
223
                text = line.decode('utf-8')
224
                if in_views:
225
                    parts = text.split('\0')
226
                    view = parts.pop(0)
227
                    views[view] = parts
228
                elif text == 'views:':
229
                    in_views = True
230
                    continue
231
                elif text.find('=') >= 0:
232
                    # must be a name-value pair
233
                    keyword, value = text.split('=', 1)
234
                    keywords[keyword] = value
235
                else:
236
                    raise ValueError("failed to deserialize views line %s",
237
                        text)
238
            return keywords, views
3586.1.2 by Ian Clatworthy
first cut of views.py
239
        except ValueError, e:
240
            raise ValueError("failed to deserialize views content %r: %s"
241
                % (view_content, e))
242
243
244
class DisabledViews(_Views):
245
    """View storage that refuses to store anything.
246
247
    This is used by older formats that can't store views.
248
    """
249
250
    def __init__(self, tree):
251
        self.tree = tree
252
253
    def supports_views(self):
254
        return False
255
256
    def _not_supported(self, *a, **k):
257
        raise errors.ViewsNotSupported(self.tree)
258
259
    get_view_info = _not_supported
260
    set_view_info = _not_supported
261
    lookup_view = _not_supported
262
    set_view = _not_supported
263
    delete_view = _not_supported
3586.1.20 by Ian Clatworthy
centralise formatting of view file lists
264
265
266
def view_display_str(view_files, encoding=None):
267
    """Get the display string for a list of view files.
268
269
    :param view_files: the list of file names
270
    :param encoding: the encoding to display the files in
271
    """
272
    if encoding is None:
273
        return ", ".join(view_files)
274
    else:
275
        return ", ".join([v.encode(encoding, 'replace') for v in view_files])