sbuild-basic-keyfile.h

00001 /* Copyright © 2005-2009  Roger Leigh <rleigh@debian.org>
00002  *
00003  * schroot is free software: you can redistribute it and/or modify it
00004  * under the terms of the GNU General Public License as published by
00005  * the Free Software Foundation, either version 3 of the License, or
00006  * (at your option) any later version.
00007  *
00008  * schroot is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  * General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program.  If not, see
00015  * <http://www.gnu.org/licenses/>.
00016  *
00017  *********************************************************************/
00018 
00019 #ifndef SBUILD_BASIC_KEYFILE_H
00020 #define SBUILD_BASIC_KEYFILE_H
00021 
00022 #include <sbuild/sbuild-i18n.h>
00023 #include <sbuild/sbuild-log.h>
00024 #include <sbuild/sbuild-keyfile-base.h>
00025 #include <sbuild/sbuild-parse-error.h>
00026 #include <sbuild/sbuild-parse-value.h>
00027 #include <sbuild/sbuild-types.h>
00028 #include <sbuild/sbuild-tr1types.h>
00029 #include <sbuild/sbuild-util.h>
00030 
00031 #include <cassert>
00032 #include <map>
00033 #include <string>
00034 #include <sstream>
00035 
00036 #include <boost/format.hpp>
00037 
00038 namespace sbuild
00039 {
00043   template <typename K>
00044   class basic_keyfile_parser
00045   {
00046   public:
00048     typedef keyfile_base::error error;
00049 
00051     basic_keyfile_parser ():
00052       group(),
00053       group_set(false),
00054       key(),
00055       key_set(false),
00056       value(),
00057       value_set(false),
00058       comment(),
00059       comment_set(false),
00060       line_number(0)
00061     {
00062     }
00063 
00065     virtual ~basic_keyfile_parser ()
00066     {
00067     }
00068 
00070     typename K::group_name_type group;
00071 
00073     bool                        group_set;
00074 
00076     typename K::key_type        key;
00077 
00079     bool                        key_set;
00080 
00082     typename K::value_type      value;
00083 
00085     bool                        value_set;
00086 
00088     typename K::comment_type    comment;
00089 
00091     bool                        comment_set;
00092 
00094     typename K::size_type       line_number;
00095 
00100     virtual void
00101     begin ()
00102     {
00103       line_number = 0;
00104     }
00105 
00116     virtual void
00117     parse_line (std::string const& line)
00118     {
00119       ++line_number;
00120     }
00121 
00126     virtual void
00127     end()
00128     {
00129     }
00130   };
00131 
00137   template <typename K, typename P = basic_keyfile_parser<K> >
00138   class basic_keyfile : public keyfile_base
00139   {
00140   public:
00142     typedef typename K::group_name_type group_name_type;
00143 
00145     typedef typename K::key_type key_type;
00146 
00148     typedef typename K::value_type value_type;
00149 
00151     typedef typename K::comment_type comment_type;
00152 
00154     typedef typename K::size_type size_type;
00155 
00157     typedef std::vector<group_name_type> group_list;
00158 
00160     typedef std::vector<value_type> value_list;
00161 
00162   private:
00164     typedef P parse_type;
00165 
00167     typedef std::tr1::tuple<key_type,value_type,comment_type,size_type>
00168     item_type;
00169 
00171     typedef std::map<key_type,item_type> item_map_type;
00172 
00174     typedef std::tr1::tuple<group_name_type,item_map_type,comment_type,size_type> group_type;
00175 
00177     typedef std::map<group_name_type,group_type> group_map_type;
00178 
00180     typedef std::vector<key_type> key_list;
00181 
00182   public:
00184     basic_keyfile ();
00185 
00191     basic_keyfile (std::string const& file);
00192 
00198     basic_keyfile (std::istream& stream);
00199 
00201     virtual ~basic_keyfile ();
00202 
00209     group_list
00210     get_groups () const;
00211 
00219     key_list
00220     get_keys (group_name_type const& group) const;
00221 
00230     void
00231     check_keys (group_name_type const& group,
00232                 key_list const&        keys) const;
00233 
00240     bool
00241     has_group (group_name_type const& group) const;
00242 
00250     bool
00251     has_key (group_name_type const& group,
00252              key_type const&        key) const;
00253 
00261     void
00262     set_group (group_name_type const& group,
00263                comment_type const&    comment);
00264 
00273     void
00274     set_group (group_name_type const& group,
00275                comment_type const&    comment,
00276                size_type              line);
00277 
00284     comment_type
00285     get_comment (group_name_type const& group) const;
00286 
00294     comment_type
00295     get_comment (group_name_type const& group,
00296                  key_type const&        key) const;
00297 
00304     size_type
00305     get_line (group_name_type const& group) const;
00306 
00314     size_type
00315     get_line (group_name_type const& group,
00316               key_type const&        key) const;
00317 
00328     template <typename T>
00329     bool
00330     get_value (group_name_type const& group,
00331                key_type const&        key,
00332                T&                     value) const
00333     {
00334       log_debug(DEBUG_INFO) << "Getting keyfile group=" << group
00335                             << ", key=" << key << std::endl;
00336       const item_type *found_item = find_item(group, key);
00337       if (found_item)
00338         {
00339           value_type const& strval(std::tr1::get<1>(*found_item));
00340           try
00341             {
00342               parse_value(strval, value);
00343               return true;
00344             }
00345           catch (parse_value_error const& e)
00346             {
00347               size_type line = get_line(group, key);
00348               if (line)
00349                 {
00350                   error ep(line, group, key, PASSTHROUGH_LGK, e);
00351                   log_exception_warning(ep);
00352                 }
00353               else
00354                 {
00355                   error ep(group, key, PASSTHROUGH_GK, e);
00356                   log_exception_warning(ep);
00357                 }
00358               return false;
00359             }
00360         }
00361       log_debug(DEBUG_NOTICE) << "key not found" << std::endl;
00362       return false;
00363     }
00364 
00377     template <typename T>
00378     bool
00379     get_value (group_name_type const& group,
00380                key_type const&        key,
00381                priority               priority,
00382                T&                     value) const
00383     {
00384       bool status = get_value(group, key, value);
00385       check_priority(group, key, priority, status);
00386       return status;
00387     }
00388 
00398     bool
00399     get_locale_string (group_name_type const& group,
00400                        key_type const&        key,
00401                        value_type&            value) const;
00402 
00414     bool
00415     get_locale_string (group_name_type const& group,
00416                        key_type const&        key,
00417                        priority               priority,
00418                        value_type&            value) const;
00419 
00430     bool
00431     get_locale_string (group_name_type const& group,
00432                        key_type const&        key,
00433                        std::string const&     locale,
00434                        value_type&            value) const;
00435 
00449     bool
00450     get_locale_string (group_name_type const& group,
00451                        key_type const&        key,
00452                        std::string const&     locale,
00453                        priority               priority,
00454                        value_type&            value) const;
00455 
00468     template <typename C>
00469     bool
00470     get_list_value (group_name_type const& group,
00471                     key_type const&        key,
00472                     C&                     container) const
00473     {
00474       value_type item_value;
00475       if (get_value(group, key, item_value))
00476         {
00477           value_list items = split_string(item_value,
00478                                           this->separator);
00479           for (typename value_list::const_iterator pos = items.begin();
00480                pos != items.end();
00481                ++pos
00482                )
00483             {
00484               typename C::value_type tmp;
00485 
00486               try
00487                 {
00488                   parse_value(*pos, tmp);
00489                 }
00490               catch (parse_value_error const& e)
00491                 {
00492                   size_type line = get_line(group, key);
00493                   if (line)
00494                     {
00495                       error ep(line, group, key, PASSTHROUGH_LGK, e);
00496                       log_exception_warning(ep);
00497                     }
00498                   else
00499                     {
00500                       error ep(group, key, PASSTHROUGH_GK, e);
00501                       log_exception_warning(ep);
00502                     }
00503                   return false;
00504                 }
00505 
00506               container.push_back(tmp);
00507             }
00508           return true;
00509         }
00510       return false;
00511     }
00512 
00527     template <typename C>
00528     bool
00529     get_list_value (group_name_type const& group,
00530                     key_type const&        key,
00531                     priority               priority,
00532                     C&                     container) const
00533     {
00534       bool status = get_list_value(group, key, container);
00535       check_priority(group, key, priority, status);
00536       return status;
00537     }
00538 
00547     template <typename T>
00548     void
00549     set_value (group_name_type const& group,
00550                key_type const&        key,
00551                T const&               value)
00552     {
00553       set_value(group, key, value, comment_type());
00554     }
00555 
00565     template <typename T>
00566     void
00567     set_value (group_name_type const& group,
00568                key_type const&        key,
00569                T const&               value,
00570                comment_type const&    comment)
00571     {
00572       set_value(group, key, value, comment, 0);
00573     }
00574 
00585     template <typename T>
00586     void
00587     set_value (group_name_type const& group,
00588                key_type const&        key,
00589                T const&               value,
00590                comment_type const&    comment,
00591                size_type              line)
00592     {
00593       std::ostringstream os;
00594       os.imbue(std::locale::classic());
00595       os << std::boolalpha << value;
00596 
00597       set_group(group, "");
00598       group_type *found_group = find_group(group);
00599       assert (found_group != 0); // should not fail
00600 
00601       item_map_type& items = std::tr1::get<1>(*found_group);
00602 
00603       typename item_map_type::iterator pos = items.find(key);
00604       if (pos != items.end())
00605         items.erase(pos);
00606       items.insert
00607         (typename item_map_type::value_type(key,
00608                                             item_type(key, os.str(),
00609                                                       comment, line)));
00610     }
00611 
00621     template <typename I>
00622     void
00623     set_list_value (group_name_type const& group,
00624                     key_type const&        key,
00625                     I                      begin,
00626                     I                      end)
00627     {
00628       set_list_value(group, key, begin, end, comment_type());
00629     }
00630 
00641     template <typename I>
00642     void
00643     set_list_value (group_name_type const& group,
00644                     key_type const&        key,
00645                     I                      begin,
00646                     I                      end,
00647                     comment_type const&    comment)
00648     {
00649       set_list_value (group, key, begin, end, comment, 0);
00650     }
00651 
00663     template <typename I>
00664     void
00665     set_list_value (group_name_type const& group,
00666                     key_type const&        key,
00667                     I                      begin,
00668                     I                      end,
00669                     comment_type const&    comment,
00670                     size_type              line)
00671     {
00672       value_type strval;
00673 
00674       for (I pos = begin; pos != end; ++ pos)
00675         {
00676           std::ostringstream os;
00677           os.imbue(std::locale::classic());
00678           os << std::boolalpha << *pos;
00679           if (os)
00680             {
00681               strval += os.str();
00682               if (pos + 1 != end)
00683                 strval += this->separator;
00684             }
00685         }
00686 
00687       set_value (group, key, strval, comment, line);
00688     }
00689 
00695     void
00696     remove_group (group_name_type const& group);
00697 
00704     void
00705     remove_key (group_name_type const& group,
00706                 key_type const&        key);
00707 
00714     basic_keyfile&
00715     operator += (basic_keyfile const& rhs);
00716 
00724     template <typename _K, typename _P>
00725     friend basic_keyfile<_K, _P>
00726     operator + (basic_keyfile<_K, _P> const& lhs,
00727                 basic_keyfile<_K, _P> const& rhs);
00728 
00736     template <class charT, class traits>
00737     friend
00738     std::basic_istream<charT,traits>&
00739     operator >> (std::basic_istream<charT,traits>& stream,
00740                  basic_keyfile&                    kf)
00741     {
00742       basic_keyfile tmp;
00743       parse_type state;
00744       std::string line;
00745 
00746       state.begin();
00747 
00748       while (std::getline(stream, line))
00749       {
00750         state.parse_line(line);
00751 
00752         // Insert group
00753         if (state.group_set)
00754           {
00755             if (tmp.has_group(state.group))
00756               throw error(state.line_number, DUPLICATE_GROUP, state.group);
00757             else
00758               tmp.set_group(state.group, state.comment, state.line_number);
00759           }
00760 
00761         // Insert item
00762         if (state.key_set && state.value_set)
00763           {
00764             if (tmp.has_key(state.group, state.key))
00765               throw error(state.line_number, state.group, DUPLICATE_KEY, state.key);
00766             else
00767               tmp.set_value(state.group, state.key, state.value, state.comment, state.line_number);
00768           }
00769       }
00770 
00771       state.end();
00772       // TODO: do inserts here as well.
00773 
00774       kf += tmp;
00775 
00776       return stream;
00777     }
00778 
00786     template <class charT, class traits>
00787     friend
00788     std::basic_ostream<charT,traits>&
00789     operator << (std::basic_ostream<charT,traits>& stream,
00790                  basic_keyfile const&              kf)
00791     {
00792       size_type group_count = 0;
00793 
00794       for (typename group_map_type::const_iterator gp = kf.groups.begin();
00795            gp != kf.groups.end();
00796            ++gp, ++group_count)
00797         {
00798           if (group_count > 0)
00799             stream << '\n';
00800 
00801           group_type const& group = gp->second;
00802           group_name_type const& groupname = std::tr1::get<0>(group);
00803           comment_type const& comment = std::tr1::get<2>(group);
00804 
00805           if (comment.length() > 0)
00806             print_comment(comment, stream);
00807 
00808           stream << '[' << groupname << ']' << '\n';
00809 
00810           item_map_type const& items(std::tr1::get<1>(group));
00811           for (typename item_map_type::const_iterator it = items.begin();
00812                it != items.end();
00813                ++it)
00814             {
00815               item_type const& item = it->second;
00816               key_type const& key(std::tr1::get<0>(item));
00817               value_type const& value(std::tr1::get<1>(item));
00818               comment_type const& comment(std::tr1::get<2>(item));
00819 
00820               if (comment.length() > 0)
00821                 print_comment(comment, stream);
00822 
00823               stream << key << '=' << value << '\n';
00824             }
00825         }
00826 
00827       return stream;
00828     }
00829 
00830   private:
00837     const group_type *
00838     find_group (group_name_type const& group) const;
00839 
00846     group_type *
00847     find_group (group_name_type const& group);
00848 
00856     const item_type *
00857     find_item (group_name_type const& group,
00858                key_type const&        key) const;
00859 
00867     item_type *
00868     find_item (group_name_type const& group,
00869                key_type const&        key);
00870 
00879     void
00880     check_priority (group_name_type const& group,
00881                     key_type const&        key,
00882                     priority               priority,
00883                     bool                   valid) const;
00884 
00896     static void
00897     print_comment (comment_type const& comment,
00898                    std::ostream&       stream);
00899 
00901     group_map_type groups;
00903     value_type     separator;
00904 
00905   public:
00918     template<class C, typename T>
00919     static void
00920     set_object_value (C const&               object,
00921                       T                (C::* method)() const,
00922                       basic_keyfile&         basic_keyfile,
00923                       group_name_type const& group,
00924                       key_type const&        key)
00925     {
00926       try
00927         {
00928           if (method)
00929             basic_keyfile.set_value(group, key, (object.*method)());
00930         }
00931       catch (std::runtime_error const& e)
00932         {
00933           throw error(group, key, PASSTHROUGH_GK, e);
00934         }
00935     }
00936 
00949     template<class C, typename T>
00950     static void
00951     set_object_value (C const&               object,
00952                       T const&         (C::* method)() const,
00953                       basic_keyfile&         basic_keyfile,
00954                       group_name_type const& group,
00955                       key_type const&        key)
00956     {
00957       try
00958         {
00959           if (method)
00960             basic_keyfile.set_value(group, key, (object.*method)());
00961         }
00962       catch (std::runtime_error const& e)
00963         {
00964           throw error(group, key, PASSTHROUGH_GK, e);
00965         }
00966     }
00967 
00981     template<class C, typename T>
00982     static void
00983     set_object_list_value (C const&               object,
00984                            T                (C::* method)() const,
00985                            basic_keyfile&         basic_keyfile,
00986                            group_name_type const& group,
00987                            key_type const&        key)
00988     {
00989       try
00990         {
00991           if (method)
00992             basic_keyfile.set_list_value(group, key,
00993                                          (object.*method)().begin(),
00994                                          (object.*method)().end());
00995         }
00996       catch (std::runtime_error const& e)
00997         {
00998           throw error(group, key, PASSTHROUGH_GK, e);
00999         }
01000     }
01001 
01016     template<class C, typename T>
01017     static void
01018     set_object_list_value (C const&               object,
01019                            T const&         (C::* method)() const,
01020                            basic_keyfile&         basic_keyfile,
01021                            group_name_type const& group,
01022                            key_type const&        key)
01023     {
01024       try
01025         {
01026           if (method)
01027             basic_keyfile.set_list_value(group, key,
01028                                          (object.*method)().begin(),
01029                                          (object.*method)().end());
01030         }
01031       catch (std::runtime_error const& e)
01032         {
01033           throw error(group, key, PASSTHROUGH_GK, e);
01034         }
01035     }
01036 
01051     template<class C, typename T>
01052     static void
01053     get_object_value (C&                      object,
01054                       void              (C::* method)(T param),
01055                       basic_keyfile const&    basic_keyfile,
01056                       group_name_type const&  group,
01057                       key_type const&         key,
01058                       basic_keyfile::priority priority)
01059     {
01060       try
01061         {
01062           T value;
01063           if (basic_keyfile.get_value(group, key, priority, value)
01064               && method)
01065             (object.*method)(value);
01066         }
01067       catch (std::runtime_error const& e)
01068         {
01069           size_type line = basic_keyfile.get_line(group, key);
01070           if (line)
01071             throw error(line, group, key, PASSTHROUGH_LGK, e);
01072           else
01073             throw error(group, key, PASSTHROUGH_GK, e);
01074         }
01075     }
01076 
01091     template<class C, typename T>
01092     static void
01093     get_object_value (C&                      object,
01094                       void              (C::* method)(T const& param),
01095                       basic_keyfile const&    basic_keyfile,
01096                       group_name_type const&  group,
01097                       key_type const&         key,
01098                       basic_keyfile::priority priority)
01099     {
01100       try
01101         {
01102           T value;
01103           if (basic_keyfile.get_value(group, key, priority, value)
01104               && method)
01105             (object.*method)(value);
01106         }
01107       catch (std::runtime_error const& e)
01108         {
01109           size_type line = basic_keyfile.get_line(group, key);
01110           if (line)
01111             throw error(line, group, key, PASSTHROUGH_LGK, e);
01112           else
01113             throw error(group, key, PASSTHROUGH_GK, e);
01114         }
01115     }
01116 
01131     template<class C, typename T>
01132     static void
01133     get_object_list_value (C&                      object,
01134                            void              (C::* method)(T param),
01135                            basic_keyfile const&    basic_keyfile,
01136                            group_name_type const&  group,
01137                            key_type const&         key,
01138                            basic_keyfile::priority priority)
01139     {
01140       try
01141         {
01142           T value;
01143           if (basic_keyfile.get_list_value(group, key, priority, value)
01144               && method)
01145             (object.*method)(value);
01146         }
01147       catch (std::runtime_error const& e)
01148         {
01149           size_type line = basic_keyfile.get_line(group, key);
01150           if (line)
01151             throw error(line, group, key, PASSTHROUGH_LGK, e);
01152           else
01153             throw error(group, key, PASSTHROUGH_GK, e);
01154           throw error(basic_keyfile.get_line(group, key),
01155                       group, key, e);
01156         }
01157     }
01158 
01174     template<class C, typename T>
01175     static void
01176     get_object_list_value (C&                      object,
01177                            void              (C::* method)(T const& param),
01178                            basic_keyfile const&    basic_keyfile,
01179                            group_name_type const&  group,
01180                            key_type const&         key,
01181                            basic_keyfile::priority priority)
01182     {
01183       try
01184         {
01185           T value;
01186           if (basic_keyfile.get_list_value(group, key, priority, value)
01187               && method)
01188             (object.*method)(value);
01189         }
01190       catch (std::runtime_error const& e)
01191         {
01192           size_type line = basic_keyfile.get_line(group, key);
01193           if (line)
01194             throw error(line, group, key, PASSTHROUGH_LGK, e);
01195           else
01196             throw error(group, key, PASSTHROUGH_GK, e);
01197           throw error(basic_keyfile.get_line(group, key),
01198                       group, key, e);
01199         }
01200     }
01201   };
01202 
01203 }
01204 
01205 #include <sbuild/sbuild-basic-keyfile.tcc>
01206 
01207 #endif /* SBUILD_BASIC_KEYFILE_H */
01208 
01209 /*
01210  * Local Variables:
01211  * mode:C++
01212  * End:
01213  */