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