~azzar1/unity/add-show-desktop-key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# IVLE - Informatics Virtual Learning Environment
# Copyright (C) 2007-2009 The University of Melbourne
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

"""Object publishing URL utilities."""

import os.path

ROOT = object() # Marker object for the root.
INF = object()

class PublishingError(Exception):
    pass

class NotFound(PublishingError):
    """The path did not resolve to an object."""
    pass

class InsufficientPathSegments(NotFound):
    """The path led to a route that expected more arguments."""
    pass

class NoPath(PublishingError):
    """There is no path from the given object to the root."""
    pass

class RouteConflict(PublishingError):
    """A route with the same discriminator is already registered."""
    pass

def _segment_path(path):
    """Split a path into its segments, after normalisation.

       >>> _segment_path('/path/to/something')
       ['path', 'to', 'something']
       >>> _segment_path('/path/to/something/')
       ['path', 'to', 'something']
       >>> _segment_path('/')
       []
    """

    segments = os.path.normpath(path).split('/')

    # Remove empty segments caused by leading and trailing seperators.
    if segments[0] == '':
        segments.pop(0)
    if segments[-1] == '':
        segments.pop()
    return segments

class Publisher(object):
    '''Publisher to resolve and generate paths.

    Maintains a registry of forward and reverse routes, dealing with paths
    to objects and views published in the URL space.
    '''

    def __init__(self, root, default='+index', viewset=None):
        self.fmap = {} # Forward map.
        self.rmap = {} # Reverse map.
        self.smap = {}
        self.srmap = {}
        self.vmap = {}
        self.vrmap = {}
        self.root = root
        self.default = default
        self.viewset = viewset

    def add_forward(self, src, segment, func, argc):
        """Register a forward (path resolution) route."""

        if src not in self.fmap:
            self.fmap[src] = {}

        # If a route already exists with the same source and name, we have a
        # conflict. We don't like conflicts.
        if segment in self.fmap[src]:
            raise RouteConflict((src, segment, func),
                                (src, segment, self.fmap[src][segment][0]))

        self.fmap[src][segment] = (func, argc)

    def add_forward_func(self, func):
        frm = func._forward_route_meta
        self.add_forward(frm['src'], frm['segment'], func, frm['argc'])

    def add_reverse(self, src, func):
        """Register a reverse (path generation) route."""

        if src in self.rmap:
             raise RouteConflict((src, func), (src, self.rmap[src]))
        self.rmap[src] = func

    def add_reverse_func(self, func):
        self.add_reverse(func._reverse_route_src, func)

    def add_view(self, src, name, cls, viewset=None):
        """Add a named view for a class, in the specified view set.

        If the name is None, the view will live immediately under the source
        object. This should be used only if you need the view to have a
        subpath -- otherwise just using a view with the default name is
        better.
        """

        if src not in self.vmap:
            self.vmap[src] = {}

        if viewset not in self.vmap[src]:
            self.vmap[src][viewset] = {}

        if src not in self.vrmap:
            self.vrmap[src] = {}

        if name in self.vmap[src][viewset] or cls in self.vrmap[src]:
            raise RouteConflict((src, name, cls, viewset),
                         (src, name, self.vmap[src][viewset][name], viewset))

        self.vmap[src][viewset][name] = cls
        self.vrmap[src][cls] = (name, viewset)

    def add_set_switch(self, segment, viewset):
        """Register a leading path segment to switch to a view set."""

        if segment in self.smap:
            raise RouteConflict((segment, viewset),
                                (segment, self.smap[segment]))

        if viewset in self.srmap:
            raise RouteConflict((segment, viewset),
                                (self.srmap[viewset], viewset))

        self.smap[segment] = viewset
        self.srmap[viewset] = segment

    def traversed_to_object(self, obj):
        """Called when the path resolver encounters an object.

        Can be overridden to perform checks on an object before
        continuing resolution. This is handy for verifying permissions.
        """
        # We do nothing by default.
        pass

    def resolve(self, path):
        """Resolve a path into an object.

        Traverses the tree of routes using the given path.
        """

        viewset = self.viewset
        todo = _segment_path(path)

        # Override the viewset if the first segment matches.
        if len(todo) > 0 and todo[0] in self.smap:
            viewset = self.smap[todo[0]]
            del todo[0]

        (obj, view, subpath) = self._traverse(todo, self.root, viewset)

        return obj, view, subpath

    def generate(self, obj, view=None, subpath=None):
        """Resolve an object into a path.

        Traverse up the tree of reverse routes, generating a path which
        resolves to the object.
        """

        # Attempt to get all the way to the top. Each reverse route should
        # return a (parent, pathsegments) tuple.
        curobj = obj
        names = []

        # None represents the root.
        while curobj not in (ROOT, self.root):
            route = self.rmap.get(type(curobj))
            if route is None:
                raise NoPath(obj, curobj)
            (curobj, newnames) = route(curobj)

            # The reverse route can return either a string of one segment,
            # or a tuple of many.
            if isinstance(newnames, basestring):
                names.insert(0, newnames)
            else:
                names = list(newnames) + list(names)

        if view is not None:
            # If we don't have the view registered for this type, we can do
            # nothing.
            if type(obj) not in self.vrmap or \
               view not in self.vrmap[type(obj)]:
                raise NoPath(obj, view)

            (viewname, viewset) = self.vrmap[type(obj)][view]

            # If the view's set isn't the default one, we need to have it in
            # the map.
            if viewset != self.viewset:
                if viewset not in self.srmap:
                    raise NoPath(obj, view)
                else:
                    names = [self.srmap[viewset]] + names

            # Generate nice URLs for the default route, if it is the last.
            if viewname != self.default:
                # Deep views may have multiple segments in their name.
                if isinstance(viewname, basestring):
                    names += [viewname]
                elif viewname[-1] == '+index' and not subpath:
                    # If the last segment of the path is the default view, we
                    # can omit it.
                    names += viewname[:-1]
                else:
                    names += viewname

        if subpath is not None:
            if isinstance(subpath, basestring):
                return os.path.join(os.path.join('/', *names), subpath)
            else:
                names += subpath
        return os.path.join('/', *names)

    def get_ancestors(self, obj):
        """Get a sequence of an object's ancestors.

        Traverse up the tree of reverse routes, taking note of all ancestors.
        """

        # Attempt to get all the way to the top. Each reverse route should
        # return a (parent, pathsegments) tuple. We don't care about
        # pathsegments in this case.
        objs = [obj]

        # None represents the root.
        while objs[0] not in (ROOT, self.root):
            route = self.rmap.get(type(objs[0]))
            if route is None:
                raise NoPath(obj, objs[0])
            objs.insert(0, route(objs[0])[0])

        return objs[1:]

    def _traverse(self, todo, obj, viewset):
        """Populate the object stack given a list of path segments.

        Traverses the forward route tree, using the given path segments.

        Intended to be used by route(), and nobody else.
        """
        while True:
            # Attempt views first, then routes.
            if type(obj) in self.vmap and \
               viewset in self.vmap[type(obj)]:
                # If there are no segments left, attempt the default view.
                # Otherwise, look for a view with the name in the first
                # remaining path segment.
                vnames = self.vmap[type(obj)][viewset]
                if None in vnames:
                    view = vnames[None]
                    remove = 0
                else:
                    view = vnames.get(
                        self.default if len(todo) == 0 else todo[0])
                    remove = 1

                if view is not None:
                    return (obj, view, tuple(todo[remove:]))
                # We have just one segment, but no view was found. Try
                # appending the default view name.
                elif len(todo) == 1:
                    # Augment it with the default view name, and look it up.
                    view = vnames.get((todo[0], self.default))
                    if view is not None:
                        return (obj, view, tuple(todo[2:]))
                # Now we must check for deep views.
                # A deep view is one that has a name consisting of
                # multiple segments. It's messier than it could be, because
                # we also allow omission of the final segment if it is the
                # default view name.
                elif len(todo) >= 2:
                    for x in range(2, len(todo) + 1):
                        view = vnames.get(tuple(todo[:x]))
                        if view is not None:
                            return (obj, view, tuple(todo[x:]))
                    view = vnames.get(tuple(todo + [self.default]))
                    if view is not None:
                        return (obj, view, tuple())

            # If there are no segments left to use, or there are no routes, we
            # get out.
            if len(todo) == 0:
                raise NotFound(obj, '+index', ())

            if type(obj) not in self.fmap:
                raise NotFound(obj, todo[0], todo[1:])

            routenames = self.fmap[type(obj)]

            if todo[0] in routenames:
                routename = todo[0]
                # The first path segment is the route identifier, so we skip
                # it when identifying arguments.
                argoffset = 1
            elif None in routenames:
                # Attempt traversal directly (with no intermediate segment)
                # as a last resort.
                routename = None
                argoffset = 0
            else:
                raise NotFound(obj, todo[0], tuple(todo[1:]))

            route, argc = routenames[routename]

            if argc is INF:
                args = todo[argoffset:]
                todo = []
            else:
                args = todo[argoffset:argc + argoffset]
                todo = todo[argc + argoffset:]

            if argc is not INF and len(args) != argc:
                # There were too few path segments left. Die.
                raise InsufficientPathSegments(
                                obj,
                                tuple(args) if len(args) != 1 else args[0],
                                tuple(todo)
                                )

            newobj = route(obj, *args)

            if newobj is None:
                raise NotFound(obj, tuple(args) if len(args) != 1 else args[0],
                               tuple(todo))

            self.traversed_to_object(newobj)

            obj = newobj