1 // Resource.hh
2 // Copyright (c) 2002-2003 Henrik Kinnunen (fluxgen at fluxbox dot org)
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21
22 #ifndef FBTK_RESOURCE_HH
23 #define FBTK_RESOURCE_HH
24
25 #include "NotCopyable.hh"
26 #include "Accessor.hh"
27 #include "XrmDatabaseHelper.hh"
28
29 #include <string>
30 #include <list>
31 #include <iostream>
32 #include <exception>
33 #include <typeinfo>
34
35 namespace FbTk {
36
37 class ResourceException: public std::exception {
38 public:
ResourceException(const std::string & err)39 ResourceException(const std::string &err):
40 m_str(err) { };
~ResourceException()41 ~ResourceException() throw() { }
what() const42 const char *what() const throw () { return m_str.c_str(); }
43 private:
44 std::string m_str;
45 };
46
47 /// Base class for resources, this is only used in ResourceManager
48 class Resource_base:private FbTk::NotCopyable
49 {
50 public:
~Resource_base()51 virtual ~Resource_base() { };
52
53 /// set from string value
54 virtual void setFromString(char const *strval) = 0;
55 /// set default value
56 virtual void setDefaultValue() = 0;
57 /// get string value
58 virtual std::string getString() const = 0;
59 /// get alternative name of this resource
altName() const60 const std::string& altName() const { return m_altname; }
61 /// get name of this resource
name() const62 const std::string& name() const { return m_name; }
63
64 protected:
Resource_base(const std::string & name,const std::string & altname)65 Resource_base(const std::string &name, const std::string &altname):
66 m_name(name), m_altname(altname)
67 { }
68
69 private:
70 std::string m_name; ///< name of this resource
71 std::string m_altname; ///< alternative name
72 };
73
74 template <typename T>
75 class Resource;
76
77 class ResourceManager
78 {
79 public:
80 typedef std::list<Resource_base *> ResourceList;
81
82 // lock specifies if the database should be opened with one level locked
83 // (useful for constructing inside initial set of constructors)
84 ResourceManager(const char *filename, bool lock_db);
85 virtual ~ResourceManager();
86
87 /// Load all resources registered to this class
88 /// @return true on success
89 virtual bool load(const char *filename);
90
91 /// Save all resouces registered to this class
92 /// @return true on success
93 virtual bool save(const char *filename, const char *mergefilename=0);
94
95
96
97 /// Add resource to list, only used in Resource<T>
98 template <class T>
99 void addResource(Resource<T> &r);
100
101 /// Remove a specific resource, only used in Resource<T>
102 template <class T>
removeResource(Resource<T> & r)103 void removeResource(Resource<T> &r) {
104 m_resourcelist.remove(&r);
105 }
106
107 /// searches for the resource with the resourcename
108 /// @return pointer to resource base on success, else 0.
109 Resource_base *findResource(const std::string &resourcename);
110 /// searches for the resource with the resourcename
111 /// @return pointer to resource base on success, else 0.
112 const Resource_base *findResource(const std::string &resourcename) const;
113
114 std::string resourceValue(const std::string &resourcename) const;
115 void setResourceValue(const std::string &resourcename, const std::string &value);
116
117 /**
118 * Will search and cast the resource to Resource<Type>,
119 * it will throw exception if it fails
120 * @return reference to resource type
121 */
122 template <typename ResourceType>
123 Resource<ResourceType> &getResource(const std::string &resource);
124
125 // this marks the database as "in use" and will avoid reloading
126 // resources unless it is zero.
127 // It returns this resource manager. Useful for passing to
128 // constructors like Object(m_rm.lock())
129 ResourceManager &lock();
130 void unlock();
131 // for debugging
lockDepth() const132 int lockDepth() const { return m_db_lock; }
dump()133 void dump() {
134 ResourceList::iterator it = m_resourcelist.begin();
135 ResourceList::iterator it_end = m_resourcelist.end();
136 for (; it != it_end; ++it) {
137 std::cerr<<(*it)->name()<<std::endl;
138 }
139 }
140 protected:
141
142 int m_db_lock;
143
144 private:
145
146 ResourceList m_resourcelist;
147
148 XrmDatabaseHelper *m_database;
149
150 std::string m_filename;
151 };
152
153
154 /// Real resource class
155 /**
156 * usage: Resource<int> someresource(resourcemanager, 10, "someresourcename", "somealternativename");
157 * and then implement setFromString and getString
158 * example:
159 * template <>
160 * void Resource<int>::setFromString(const char *str) {
161 * *(*this) = atoi(str);
162 * }
163 */
164 template <typename T>
165 class Resource:public Resource_base, public Accessor<T> {
166 public:
167 typedef T Type;
Resource(ResourceManager & rm,T val,const std::string & name,const std::string & altname)168 Resource(ResourceManager &rm, T val, const std::string &name, const std::string &altname):
169 Resource_base(name, altname), m_value(val), m_defaultval(val), m_rm(rm) {
170 m_rm.addResource(*this); // add this to resource handler
171 }
~Resource()172 virtual ~Resource() {
173 m_rm.removeResource(*this); // remove this from resource handler
174 }
175
setDefaultValue()176 void setDefaultValue() { m_value = m_defaultval; }
177 /// sets resource from string, specialized, must be implemented
178 void setFromString(const char *strval);
operator =(const T & newvalue)179 Accessor<T> &operator =(const T& newvalue) { m_value = newvalue; return *this;}
180 /// specialized, must be implemented
181 /// @return string value of resource
182 std::string getString() const;
183
operator T() const184 operator T() const { return m_value; }
get()185 T& get() { return m_value; }
operator *()186 T& operator*() { return m_value; }
operator *() const187 const T& operator*() const { return m_value; }
operator ->()188 T *operator->() { return &m_value; }
operator ->() const189 const T *operator->() const { return &m_value; }
190 private:
191 T m_value, m_defaultval;
192 ResourceManager &m_rm;
193 };
194
195
196 // add the resource and load its value
197 template <class T>
addResource(Resource<T> & r)198 void ResourceManager::addResource(Resource<T> &r) {
199 m_resourcelist.push_back(&r);
200 m_resourcelist.unique();
201
202 // lock ensures that the database is loaded.
203 lock();
204
205 if (m_database == 0) {
206 unlock();
207 return;
208 }
209
210 XrmValue value;
211 char *value_type;
212
213 // now, load the value for this resource
214 if (XrmGetResource(**m_database, r.name().c_str(),
215 r.altName().c_str(), &value_type, &value)) {
216 r.setFromString(value.addr);
217 } else {
218 std::cerr<<"Failed to read: "<<r.name()<<std::endl;
219 std::cerr<<"Setting default value"<<std::endl;
220 r.setDefaultValue();
221 }
222
223 unlock();
224 }
225
226
227 template <typename ResourceType>
getResource(const std::string & resname)228 Resource<ResourceType> &ResourceManager::getResource(const std::string &resname) {
229 Resource_base *res = findResource(resname);
230 if (res == 0) {
231 throw ResourceException("Could not find resource \"" +
232 resname + "\"");
233 }
234
235 Resource<ResourceType> *res_type =
236 dynamic_cast<Resource<ResourceType> *>(res);
237 if (res_type == 0) {
238 throw ResourceException("Could not convert resource \"" +
239 resname +
240 "\"");
241 }
242
243 return *res_type;
244 }
245
246 } // end namespace FbTk
247
248 #endif // FBTK_RESOURCE_HH
249