ac46759b885a7b73e1cf28143b05c334edb5a474
[gregoa/zavai.git] / zavai / registry.py
1 # registry - zavai resource registry
2 #
3 # Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19 import zavai
20
21 def get_parent(s):
22     "Get the parent name for s"
23     pos = s.rfind(".")
24     if pos == -1: return None
25     res = s[:pos]
26     if res == "menu": return None
27     return res
28
29 def default_label(s):
30     "Compute a default label given the last element of a path"
31     pos = s.rfind(".")
32     if pos == -1: return s.capitalize()
33     return s[pos+1:].capitalize()
34
35
36 class Registry(object):
37     """Collection of resources.
38
39     Various factories can be registered by name on the registry. Then when an
40     object is requested for the first time, it is created using the factory.
41     When it is requested again, the existing object is reused.
42     """
43
44     def __init__(self):
45         self.factories = dict()
46         self.objects = dict()
47         self.labels = dict()
48
49     def register(self, name, obj, label = None):
50         """Register an object at the given path.
51
52         Name the path to this object, like "menu.gps.monitor".
53         """
54         if name in self.objects:
55             return KeyError("%s is already registered", name)
56         zavai.info("Registering", name)
57         self.objects[name] = obj
58         if label is not None: self.labels[name] = label
59
60         if name.startswith("menu."):
61             self.add_to_menu(name)
62
63     def register_factory(self, name, fac, label = None):
64         """Register an object factory at the given path.
65
66         Name the path to this object, like "menu.gps.monitor".
67         """
68         if name in self.factories:
69             return KeyError("Factory %s is already registered", name)
70         zavai.info("Registering factory", name)
71         self.factories[name] = fac
72         if label is not None: self.labels[name] = label
73
74         if name.startswith("menu."):
75             self.add_to_menu(name)
76
77     def add_to_menu(self, name):
78         "Add the applet with the given name to the menu structure"
79         parent = get_parent(name)
80         if parent is not None:
81             zavai.info("Add to menu", name, parent)
82             menu = self.menu(parent)
83             menu.add_child(zavai.LinkButton(self, name, self.label(name)))
84
85     def label(self, name):
86         "Return the label for the object with the given name"
87         res = self.labels.get(name)
88         if res is not None:
89             return res
90         return default_label(name)
91
92     def resource(self, name):
93         """Get a resource from the registry.
94
95         If no resource exists at `name` but there is a factory, instantiate the
96         object using the factory.
97
98         If not even a factory exists at `name`, returns None.
99         """
100         res = self.objects.get(name, None)
101         if res is None:
102             fac = self.factories.get(name, None)
103             if fac is not None:
104                 res = self.objects[name] = fac(self, name)
105         return res
106
107     def menu(self, name):
108         """Get a menu resource, automatically creating it if it is missing.
109
110         Menus are created automatically linked to a parent menu, according to
111         the hierarchy in `name`.
112         """
113         res = self.resource(name)
114         if res is None:
115             # Check if it is a toplevel menu
116             if name.startswith("menu."):
117                 parent = get_parent(name[5:])
118                 if parent is not None:
119                     parent = "menu." + parent
120             else:
121                 parent = get_parent(name)
122
123             res = zavai.Menu(self, name, parent)
124             self.register(name, res)
125         return res
126
127     def shutdown(self):
128         """Shut down all objects in this Registry.
129
130         After shutting down, all objects cannot be used anymore"""
131         for o in self.objects.itervalues():
132             if isinstance(o, Resource):
133                 o.shutdown()
134         self.objects.clear()
135
136 class Resource(object):
137     def shutdown(self):
138         """Shut down this resource.
139
140         Normally one does nothing here, but it is important to give resources a
141         chance to do cleanup when the program quits.
142
143         This can be used for tasks like closing the tags on a GPX track,
144         releasing a FSO resource, restoring mixer settings and so on.
145         """
146         pass