/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/views.py

  • Committer: Ian Clatworthy
  • Date: 2008-12-15 06:18:29 UTC
  • mfrom: (3905 +trunk)
  • mto: (3586.1.23 views-ui)
  • mto: This revision was merged to the branch mainline in revision 4030.
  • Revision ID: ian.clatworthy@canonical.com-20081215061829-c8qwa93g71u9fsh5
merge bzr.dev 3905

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
19
Views are contained within a working tree and normally constructed 
 
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+)')
 
34
_VIEWS_FORMAT1_MARKER = "Bazaar views format 1\n"
 
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
 
 
53
    where X is an integer number. Version 1 format is stored as follows:
 
54
 
 
55
     * the line after the format marker holds the name of the current view
 
56
 
 
57
     * subsequent lines hold view definitions, one per line is the format
 
58
 
 
59
       name file1 file2 ...
 
60
 
 
61
    where the fields are separated by a nul character (\0). The views file
 
62
    is encoded in utf-8.
 
63
    """
 
64
 
 
65
    def __init__(self, tree):
 
66
        self.tree = tree
 
67
        self._loaded = False
 
68
        self._current = None
 
69
        self._views = {}
 
70
 
 
71
    def supports_views(self):
 
72
        return True
 
73
 
 
74
    def get_view_info(self):
 
75
        """Get the current view and dictionary of views.
 
76
 
 
77
        :return: current, views where
 
78
          current = the name of the current view or None if no view is enabled
 
79
          views = a map from view name to list of files/directories
 
80
        """
 
81
        self._load_view_info()
 
82
        return self._current, self._views
 
83
 
 
84
    def set_view_info(self, current, views):
 
85
        """Set the current view and dictionary of views.
 
86
 
 
87
        :param current: the name of the current view or None if no view is
 
88
          enabled
 
89
        :param views: a map from view name to list of files/directories
 
90
        """
 
91
        if current is not None and current not in views:
 
92
            raise errors.NoSuchView(current)
 
93
        self.tree.lock_write()
 
94
        try:
 
95
            self._current = current
 
96
            self._views = views
 
97
            self._save_view_info()
 
98
        finally:
 
99
            self.tree.unlock()
 
100
 
 
101
    def lookup_view(self, view_name=None):
 
102
        """Return the contents of a view.
 
103
        
 
104
        :param view_Name: name of the view or None to lookup the current view
 
105
        :return: the list of files/directories in the requested view
 
106
        """
 
107
        self._load_view_info()
 
108
        try:
 
109
            if view_name is None:
 
110
                if self._current:
 
111
                    view_name = self._current
 
112
                else:
 
113
                    return []
 
114
            return self._views[view_name]
 
115
        except KeyError:
 
116
            raise errors.NoSuchView(view_name)
 
117
 
 
118
    def set_view(self, view_name, view_files, make_current=True):
 
119
        """Add or update a view definition.
 
120
        
 
121
        :param view_name: the name of the view
 
122
        :param view_files: the list of files/directories in the view
 
123
        :param make_current: make this view the current one or not
 
124
        """
 
125
        self.tree.lock_write()
 
126
        try:
 
127
            self._load_view_info()
 
128
            self._views[view_name] = view_files
 
129
            if make_current:
 
130
                self._current = view_name
 
131
            self._save_view_info()
 
132
        finally:
 
133
            self.tree.unlock()
 
134
 
 
135
    def delete_view(self, view_name):
 
136
        """Delete a view definition.
 
137
 
 
138
        If the view deleted is the current one, the current view is reset.
 
139
        """
 
140
        self.tree.lock_write()
 
141
        try:
 
142
            self._load_view_info()
 
143
            try:
 
144
                del self._views[view_name]
 
145
            except KeyError:
 
146
                raise errors.NoSuchView(view_name)
 
147
            if view_name == self._current:
 
148
                self._current = None
 
149
            self._save_view_info()
 
150
        finally:
 
151
            self.tree.unlock()
 
152
 
 
153
    def _save_view_info(self):
 
154
        """Save the current view and all view definitions.
 
155
 
 
156
        Be sure to have initialised self._current and self._views before
 
157
        calling this method.
 
158
        """
 
159
        self.tree.lock_write()
 
160
        try:
 
161
            self.tree._transport.put_bytes('views',
 
162
                self._serialize_view_content(self._current, self._views))
 
163
        finally:
 
164
            self.tree.unlock()
 
165
 
 
166
    def _load_view_info(self):
 
167
        """Load the current view and dictionary of view definitions."""
 
168
        if not self._loaded:
 
169
            self.tree.lock_read()
 
170
            try:
 
171
                try:
 
172
                    view_content = self.tree._transport.get_bytes('views')
 
173
                except errors.NoSuchFile, e:
 
174
                    self._current, self._views = None, {}
 
175
                else:
 
176
                    self._current, self._views = \
 
177
                        self._deserialize_view_content(view_content)
 
178
            finally:
 
179
                self.tree.unlock()
 
180
            self._loaded = True
 
181
 
 
182
    def _serialize_view_content(self, current, view_dict):
 
183
        """Convert a current view and view dictionary into a stream."""
 
184
        lines = [_VIEWS_FORMAT1_MARKER]
 
185
        if current is None:
 
186
            lines.append("\n")
 
187
        else:
 
188
            lines.append((current + "\n").encode('utf-8'))
 
189
        for view in sorted(view_dict):
 
190
            view_data = "%s\0%s\n" % (view, "\0".join(view_dict[view]))
 
191
            lines.append(view_data.encode('utf-8'))
 
192
        return "".join(lines)
 
193
 
 
194
    def _deserialize_view_content(self, view_content):
 
195
        """Convert a stream into a current view and dictionary of views."""
 
196
        # as a special case to make initialization easy, an empty definition
 
197
        # maps to no current view and an empty view dictionary
 
198
        if view_content == '':
 
199
            return None, {}
 
200
        lines = view_content.splitlines()
 
201
        match = _VIEWS_FORMAT_MARKER_RE.match(lines[0])
 
202
        if not match:
 
203
            raise ValueError(
 
204
                "format marker missing from top of views file")
 
205
        elif match.group(1) != '1':
 
206
            raise ValueError(
 
207
                "cannot decode views format %s" % match.group(1))
 
208
        try:
 
209
            current = lines[1].decode('utf-8')
 
210
            if current == '':
 
211
                current = None
 
212
            views = {}
 
213
            for line in lines[2:]:
 
214
                parts = line.decode('utf-8').split('\0')
 
215
                view = parts.pop(0)
 
216
                views[view] = parts
 
217
            return current, views
 
218
        except ValueError, e:
 
219
            raise ValueError("failed to deserialize views content %r: %s"
 
220
                % (view_content, e))
 
221
 
 
222
 
 
223
class DisabledViews(_Views):
 
224
    """View storage that refuses to store anything.
 
225
 
 
226
    This is used by older formats that can't store views.
 
227
    """
 
228
 
 
229
    def __init__(self, tree):
 
230
        self.tree = tree
 
231
 
 
232
    def supports_views(self):
 
233
        return False
 
234
 
 
235
    def _not_supported(self, *a, **k):
 
236
        raise errors.ViewsNotSupported(self.tree)
 
237
 
 
238
    get_view_info = _not_supported
 
239
    set_view_info = _not_supported
 
240
    lookup_view = _not_supported
 
241
    set_view = _not_supported
 
242
    delete_view = _not_supported