00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifndef PEEKABOT_LEXICAL_MAP_HH_INCLUDED
00023 #define PEEKABOT_LEXICAL_MAP_HH_INCLUDED
00024
00025
00026 #include "Types.hh"
00027
00028 #include <map>
00029 #include <string>
00030 #include <vector>
00031 #include <set>
00032 #include <list>
00033 #include <stdexcept>
00034 #include <boost/lexical_cast.hpp>
00035 #include <boost/algorithm/string/trim.hpp>
00036 #include <boost/algorithm/string/split.hpp>
00037 #include <boost/algorithm/string/classification.hpp>
00038 #include <boost/filesystem/path.hpp>
00039
00040
00041 namespace peekabot
00042 {
00043 namespace detail
00044 {
00045 template<class S, class T>
00046 inline void lexical_assign(S &lhs, const T &rhs)
00047 {
00048 lhs = boost::lexical_cast<S>(rhs);
00049 }
00050
00051 template<>
00052 inline void lexical_assign(bool &lhs, const std::string &rhs)
00053 {
00054 if( rhs == "true" || rhs == "on" ||
00055 rhs == "enable" || rhs == "1" )
00056 lhs = true;
00057 else if( rhs == "false" || rhs == "off" ||
00058 rhs == "disable" || rhs == "0" )
00059 lhs = false;
00060 else
00061 throw boost::bad_lexical_cast();
00062 }
00063
00064 template<class T>
00065 inline void lexical_assign(std::list<T> &lhs, const std::string &rhs)
00066 {
00067 lhs.clear();
00068
00069 std::vector<std::string> components;
00070 boost::split( components, rhs, boost::is_any_of(",") );
00071
00072 for( size_t i = 0; i < components.size(); ++i )
00073 {
00074 boost::trim(components[i]);
00075 if( !components[i].empty() )
00076 {
00077 T tmp;
00078 lexical_assign(tmp, components[i]);
00079 lhs.push_back(tmp);
00080 }
00081 }
00082 }
00083
00084 template<class T>
00085 inline void lexical_assign(std::string &lhs, const std::list<T> &rhs)
00086 {
00087 lhs = "";
00088 for( typename std::list<T>::const_iterator it = rhs.begin();
00089 it != rhs.end(); ++it )
00090 {
00091 std::string tmp;
00092 lexical_assign(tmp, *it);
00093 if( it != rhs.begin() )
00094 lhs += ", ";
00095 lhs += tmp;
00096 }
00097 }
00098
00099
00100
00101 template<> inline void lexical_assign(
00102 boost::filesystem::path &lhs, const std::string &rhs)
00103 {
00104 lhs = rhs;
00105 }
00106
00107 template<> inline void lexical_assign(
00108 RGBColor &lhs, const std::string &rhs)
00109 {
00110 std::vector<std::string> components;
00111 boost::split( components, rhs, boost::is_any_of(",") );
00112
00113 if( components.size() != 3 )
00114 throw std::runtime_error(
00115 "Colors must be in the format R,G,B, "
00116 "where 0 <= R,G,B <= 1");
00117
00118 for( std::size_t i = 0; i < 3; ++i )
00119 boost::trim(components[i]);
00120
00121 float r = boost::lexical_cast<float>(components[0]);
00122 float g = boost::lexical_cast<float>(components[1]);
00123 float b = boost::lexical_cast<float>(components[2]);
00124
00125 if( r < 0 || r > 1 || g < 0 || g > 1 || b < 0 || b > 1 )
00126 throw std::runtime_error(
00127 "Colors must be in the format R,G,B, "
00128 "where 0 <= R,G,B <= 1");
00129
00130 lhs = RGBColor(r, g, b);
00131 }
00132 }
00133
00134 class LexicalMap
00135 {
00136 typedef std::map<std::string, std::string> OptionMap;
00137
00138 public:
00139 typedef std::string KeyType;
00140
00141 const std::string &get_raw(const KeyType &key) const
00142 {
00143 OptionMap::const_iterator it = m_options.find(key);
00144 if( it == m_options.end() )
00145 throw std::runtime_error("Key '" + key + "' not found");
00146 return it->second;
00147 }
00148
00149 template<typename T>
00150 T get(const KeyType &key) const
00151 {
00152 const std::string &val = get_raw(key);
00153 try
00154 {
00155 T ret;
00156 detail::lexical_assign(ret, val);
00157 return ret;
00158 }
00159 catch(...)
00160 {
00161 throw std::runtime_error(
00162 "Malformed value '" + val +
00163 "' for key '" + key + "'");
00164 }
00165 }
00166
00167 inline void set_raw(const KeyType &key, const std::string &val)
00168 {
00169 m_options[key] = val;
00170 }
00171
00172 template<typename T>
00173 void set(const KeyType &key, const T &val)
00174 {
00175 std::string raw;
00176 detail::lexical_assign(raw, val);
00177 set_raw(key, raw);
00178 }
00179
00180 inline void erase(const KeyType &key)
00181 {
00182 m_options.erase(key);
00183 }
00184
00185 inline void clear()
00186 {
00187 m_options.clear();
00188 }
00189
00190 inline std::size_t count(const KeyType &key) const
00191 {
00192 return m_options.count(key);
00193 }
00194
00195 template<typename T>
00196 bool has_type(const KeyType &key) const
00197 {
00198 if( !count(key) )
00199 {
00200 throw std::runtime_error("Key '" + key + "' not found");
00201 }
00202 else
00203 {
00204 try
00205 {
00206 T tmp;
00207 detail::lexical_assign(tmp, get_raw(key));
00208 return true;
00209 }
00210 catch(...)
00211 {
00212 return false;
00213 }
00214 }
00215 }
00216
00217 typedef OptionMap::const_iterator const_iterator;
00218
00219 inline const_iterator begin() const
00220 {
00221 return m_options.begin();
00222 }
00223
00224 inline const_iterator end() const
00225 {
00226 return m_options.end();
00227 }
00228
00229 protected:
00230 OptionMap m_options;
00231 };
00232 }
00233
00234
00235 #endif // PEEKABOT_LEXICAL_MAP_HH_INCLUDED