Fix some indenting
[public/pos.git] / database / ConfigFile.h
1 // ConfigFile.h\r
2 // Class for reading named values from configuration files\r
3 // Richard J. Wagner  v2.1  24 May 2004  wagnerr@umich.edu\r
4 \r
5 // Copyright (c) 2004 Richard J. Wagner\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to\r
9 // deal in the Software without restriction, including without limitation the\r
10 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\r
11 // sell copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\r
23 // IN THE SOFTWARE.\r
24 \r
25 // Typical usage\r
26 // -------------\r
27 // \r
28 // Given a configuration file "settings.inp":\r
29 //   atoms  = 25\r
30 //   length = 8.0  # nanometers\r
31 //   name = Reece Surcher\r
32 // \r
33 // Named values are read in various ways, with or without default values:\r
34 //   ConfigFile config( "settings.inp" );\r
35 //   int atoms = config.read<int>( "atoms" );\r
36 //   double length = config.read( "length", 10.0 );\r
37 //   string author, title;\r
38 //   config.readInto( author, "name" );\r
39 //   config.readInto( title, "title", string("Untitled") );\r
40 // \r
41 // See file example.cpp for more examples.\r
42 \r
43 #ifndef CONFIGFILE_H\r
44 #define CONFIGFILE_H\r
45 \r
46 #include <string>\r
47 #include <map>\r
48 #include <iostream>\r
49 #include <fstream>\r
50 #include <sstream>\r
51 \r
52 using std::string;\r
53 \r
54 class ConfigFile {\r
55 // Data\r
56 protected:\r
57         string myDelimiter;  // separator between key and value\r
58         string myComment;    // separator between value and comments\r
59         string mySentry;     // optional string to signal end of file\r
60         std::map<string,string> myContents;  // extracted keys and values\r
61         \r
62         typedef std::map<string,string>::iterator mapi;\r
63         typedef std::map<string,string>::const_iterator mapci;\r
64 \r
65 // Methods\r
66 public:\r
67         ConfigFile( string filename,\r
68                     string delimiter = "=",\r
69                     string comment = "#",\r
70                                 string sentry = "EndConfigFile" );\r
71         ConfigFile();\r
72         \r
73         // Search for key and read value or optional default value\r
74         template<class T> T read( const string& key ) const;  // call as read<T>\r
75         template<class T> T read( const string& key, const T& value ) const;\r
76         template<class T> bool readInto( T& var, const string& key ) const;\r
77         template<class T>\r
78         bool readInto( T& var, const string& key, const T& value ) const;\r
79         \r
80         // Modify keys and values\r
81         template<class T> void add( string key, const T& value );\r
82         void remove( const string& key );\r
83         \r
84         // Check whether key exists in configuration\r
85         bool keyExists( const string& key ) const;\r
86         \r
87         // Check or change configuration syntax\r
88         string getDelimiter() const { return myDelimiter; }\r
89         string getComment() const { return myComment; }\r
90         string getSentry() const { return mySentry; }\r
91         string setDelimiter( const string& s )\r
92                 { string old = myDelimiter;  myDelimiter = s;  return old; }  \r
93         string setComment( const string& s )\r
94                 { string old = myComment;  myComment = s;  return old; }\r
95         \r
96         // Write or read configuration\r
97         friend std::ostream& operator<<( std::ostream& os, const ConfigFile& cf );\r
98         friend std::istream& operator>>( std::istream& is, ConfigFile& cf );\r
99         \r
100 protected:\r
101         template<class T> static string T_as_string( const T& t );\r
102         template<class T> static T string_as_T( const string& s );\r
103         static void trim( string& s );\r
104 \r
105 \r
106 // Exception types\r
107 public:\r
108         struct file_not_found {\r
109                 string filename;\r
110                 file_not_found( const string& filename_ = string() )\r
111                         : filename(filename_) {} };\r
112         struct key_not_found {  // thrown only by T read(key) variant of read()\r
113                 string key;\r
114                 key_not_found( const string& key_ = string() )\r
115                         : key(key_) {} };\r
116 };\r
117 \r
118 \r
119 /* static */\r
120 template<class T>\r
121 string ConfigFile::T_as_string( const T& t )\r
122 {\r
123         // Convert from a T to a string\r
124         // Type T must support << operator\r
125         std::ostringstream ost;\r
126         ost << t;\r
127         return ost.str();\r
128 }\r
129 \r
130 \r
131 /* static */\r
132 template<class T>\r
133 T ConfigFile::string_as_T( const string& s )\r
134 {\r
135         // Convert from a string to a T\r
136         // Type T must support >> operator\r
137         T t;\r
138         std::istringstream ist(s);\r
139         ist >> t;\r
140         return t;\r
141 }\r
142 \r
143 \r
144 /* static */\r
145 template<>\r
146 inline string ConfigFile::string_as_T<string>( const string& s )\r
147 {\r
148         // Convert from a string to a string\r
149         // In other words, do nothing\r
150         return s;\r
151 }\r
152 \r
153 \r
154 /* static */\r
155 template<>\r
156 inline bool ConfigFile::string_as_T<bool>( const string& s )\r
157 {\r
158         // Convert from a string to a bool\r
159         // Interpret "false", "F", "no", "n", "0" as false\r
160         // Interpret "true", "T", "yes", "y", "1", "-1", or anything else as true\r
161         bool b = true;\r
162         string sup = s;\r
163         for( string::iterator p = sup.begin(); p != sup.end(); ++p )\r
164                 *p = toupper(*p);  // make string all caps\r
165         if( sup==string("FALSE") || sup==string("F") ||\r
166             sup==string("NO") || sup==string("N") ||\r
167             sup==string("0") || sup==string("NONE") )\r
168                 b = false;\r
169         return b;\r
170 }\r
171 \r
172 \r
173 template<class T>\r
174 T ConfigFile::read( const string& key ) const\r
175 {\r
176         // Read the value corresponding to key\r
177         mapci p = myContents.find(key);\r
178         if( p == myContents.end() ) throw key_not_found(key);\r
179         return string_as_T<T>( p->second );\r
180 }\r
181 \r
182 \r
183 template<class T>\r
184 T ConfigFile::read( const string& key, const T& value ) const\r
185 {\r
186         // Return the value corresponding to key or given default value\r
187         // if key is not found\r
188         mapci p = myContents.find(key);\r
189         if( p == myContents.end() ) return value;\r
190         return string_as_T<T>( p->second );\r
191 }\r
192 \r
193 \r
194 template<class T>\r
195 bool ConfigFile::readInto( T& var, const string& key ) const\r
196 {\r
197         // Get the value corresponding to key and store in var\r
198         // Return true if key is found\r
199         // Otherwise leave var untouched\r
200         mapci p = myContents.find(key);\r
201         bool found = ( p != myContents.end() );\r
202         if( found ) var = string_as_T<T>( p->second );\r
203         return found;\r
204 }\r
205 \r
206 \r
207 template<class T>\r
208 bool ConfigFile::readInto( T& var, const string& key, const T& value ) const\r
209 {\r
210         // Get the value corresponding to key and store in var\r
211         // Return true if key is found\r
212         // Otherwise set var to given default\r
213         mapci p = myContents.find(key);\r
214         bool found = ( p != myContents.end() );\r
215         if( found )\r
216                 var = string_as_T<T>( p->second );\r
217         else\r
218                 var = value;\r
219         return found;\r
220 }\r
221 \r
222 \r
223 template<class T>\r
224 void ConfigFile::add( string key, const T& value )\r
225 {\r
226         // Add a key with given value\r
227         string v = T_as_string( value );\r
228         trim(key);\r
229         trim(v);\r
230         myContents[key] = v;\r
231         return;\r
232 }\r
233 \r
234 #endif  // CONFIGFILE_H\r
235 \r
236 // Release notes:\r
237 // v1.0  21 May 1999\r
238 //   + First release\r
239 //   + Template read() access only through non-member readConfigFile()\r
240 //   + ConfigurationFileBool is only built-in helper class\r
241 // \r
242 // v2.0  3 May 2002\r
243 //   + Shortened name from ConfigurationFile to ConfigFile\r
244 //   + Implemented template member functions\r
245 //   + Changed default comment separator from % to #\r
246 //   + Enabled reading of multiple-line values\r
247 // \r
248 // v2.1  24 May 2004\r
249 //   + Made template specializations inline to avoid compiler-dependent linkage\r
250 //   + Allowed comments within multiple-line values\r
251 //   + Enabled blank line termination for multiple-line values\r
252 //   + Added optional sentry to detect end of configuration file\r
253 //   + Rewrote messy trimWhitespace() function as elegant trim()\r