| 1 |  | 
|---|
| 2 | #ifndef DATETIME_PERIOD_PARSER_HPP___ | 
|---|
| 3 | #define DATETIME_PERIOD_PARSER_HPP___ | 
|---|
| 4 |  | 
|---|
| 5 | /* Copyright (c) 2002-2004 CrystalClear Software, Inc. | 
|---|
| 6 |  * Use, modification and distribution is subject to the  | 
|---|
| 7 |  * Boost Software License, Version 1.0. (See accompanying | 
|---|
| 8 |  * file LICENSE-1.0 or http://www.boost.org/LICENSE-1.0) | 
|---|
| 9 |  * Author: Jeff Garland, Bart Garst | 
|---|
| 10 |  * $Date: 2005/06/06 10:36:46 $ | 
|---|
| 11 |  */ | 
|---|
| 12 |  | 
|---|
| 13 | #include "boost/date_time/string_parse_tree.hpp" | 
|---|
| 14 | #include "boost/date_time/string_convert.hpp" | 
|---|
| 15 |  | 
|---|
| 16 |  | 
|---|
| 17 | namespace boost { namespace date_time { | 
|---|
| 18 |  | 
|---|
| 19 |  | 
|---|
| 20 |   //! Not a facet, but a class used to specify and control period parsing  | 
|---|
| 21 |   /*! Provides settings for the following: | 
|---|
| 22 |    *   - period_separator -- default '/' | 
|---|
| 23 |    *   - period_open_start_delimeter -- default '[' | 
|---|
| 24 |    *   - period_open_range_end_delimeter -- default ')'  | 
|---|
| 25 |    *   - period_closed_range_end_delimeter -- default ']'  | 
|---|
| 26 |    *   - display_as_open_range, display_as_closed_range -- default closed_range | 
|---|
| 27 |    * | 
|---|
| 28 |    *  For a typical date_period, the contents of the input stream would be | 
|---|
| 29 |    *@code | 
|---|
| 30 |    *  [2004-Jan-04/2004-Feb-01] | 
|---|
| 31 |    *@endcode | 
|---|
| 32 |    * where the date format is controlled by the date facet | 
|---|
| 33 |    */ | 
|---|
| 34 |   template<class date_type, typename CharT> | 
|---|
| 35 |   class period_parser {  | 
|---|
| 36 |   public: | 
|---|
| 37 |     typedef std::basic_string<CharT> string_type; | 
|---|
| 38 |     typedef CharT                    char_type; | 
|---|
| 39 |     //typedef typename std::basic_string<char_type>::const_iterator const_itr_type; | 
|---|
| 40 |     typedef std::istreambuf_iterator<CharT> stream_itr_type; | 
|---|
| 41 |     typedef string_parse_tree<CharT> parse_tree_type; | 
|---|
| 42 |     typedef typename parse_tree_type::parse_match_result_type match_results; | 
|---|
| 43 |     typedef std::vector<std::basic_string<CharT> > collection_type; | 
|---|
| 44 |      | 
|---|
| 45 |     static const char_type default_period_separator[2]; | 
|---|
| 46 |     static const char_type default_period_start_delimeter[2]; | 
|---|
| 47 |     static const char_type default_period_open_range_end_delimeter[2]; | 
|---|
| 48 |     static const char_type default_period_closed_range_end_delimeter[2]; | 
|---|
| 49 |  | 
|---|
| 50 |     enum period_range_option { AS_OPEN_RANGE, AS_CLOSED_RANGE }; | 
|---|
| 51 |  | 
|---|
| 52 |     //! Constructor that sets up period parser options | 
|---|
| 53 |     period_parser(period_range_option range_option = AS_CLOSED_RANGE,  | 
|---|
| 54 |                   const char_type* const period_separator = default_period_separator,  | 
|---|
| 55 |                   const char_type* const period_start_delimeter = default_period_start_delimeter, | 
|---|
| 56 |                   const char_type* const period_open_range_end_delimeter = default_period_open_range_end_delimeter, | 
|---|
| 57 |                   const char_type* const period_closed_range_end_delimeter = default_period_closed_range_end_delimeter) | 
|---|
| 58 |       : m_range_option(range_option) | 
|---|
| 59 |     { | 
|---|
| 60 |       delimiters.push_back(string_type(period_separator)); | 
|---|
| 61 |       delimiters.push_back(string_type(period_start_delimeter)); | 
|---|
| 62 |       delimiters.push_back(string_type(period_open_range_end_delimeter)); | 
|---|
| 63 |       delimiters.push_back(string_type(period_closed_range_end_delimeter)); | 
|---|
| 64 |     } | 
|---|
| 65 |  | 
|---|
| 66 |     period_parser(const period_parser<date_type,CharT>& p_parser) | 
|---|
| 67 |     { | 
|---|
| 68 |       this->delimiters = p_parser.delimiters; | 
|---|
| 69 |       this->m_range_option = p_parser.m_range_option; | 
|---|
| 70 |     } | 
|---|
| 71 |  | 
|---|
| 72 |     period_range_option range_option() const | 
|---|
| 73 |     { | 
|---|
| 74 |       return m_range_option; | 
|---|
| 75 |     } | 
|---|
| 76 |     void range_option(period_range_option option) | 
|---|
| 77 |     { | 
|---|
| 78 |       m_range_option = option; | 
|---|
| 79 |     } | 
|---|
| 80 |     collection_type delimiter_strings() const | 
|---|
| 81 |     { | 
|---|
| 82 |       return delimiters; | 
|---|
| 83 |     } | 
|---|
| 84 |     void delimiter_strings(const string_type& separator, | 
|---|
| 85 |                            const string_type& start_delim, | 
|---|
| 86 |                            const string_type& open_end_delim, | 
|---|
| 87 |                            const string_type& closed_end_delim) | 
|---|
| 88 |     { | 
|---|
| 89 |       delimiters.clear(); | 
|---|
| 90 |       delimiters.push_back(separator); | 
|---|
| 91 |       delimiters.push_back(start_delim); | 
|---|
| 92 |       delimiters.push_back(open_end_delim); | 
|---|
| 93 |       delimiters.push_back(closed_end_delim); | 
|---|
| 94 |     } | 
|---|
| 95 |  | 
|---|
| 96 |     //! Generic code to parse a period -- no matter the period type. | 
|---|
| 97 |     /*! This generic code will parse any period using a facet to | 
|---|
| 98 |      *  to get the 'elements'.  For example, in the case of a date_period | 
|---|
| 99 |      *  the elements will be instances of a date which will be parsed | 
|---|
| 100 |      *  according the to setup in the passed facet parameter. | 
|---|
| 101 |      *  | 
|---|
| 102 |      *  The steps for parsing a period are always the same: | 
|---|
| 103 |      *  - consume the start delimiter | 
|---|
| 104 |      *  - get start element | 
|---|
| 105 |      *  - consume the separator  | 
|---|
| 106 |      *  - get either last or end element depending on range settings | 
|---|
| 107 |      *  - consume the end delimeter depending on range settings | 
|---|
| 108 |      * | 
|---|
| 109 |      *  Thus for a typical date period the contents of the input stream | 
|---|
| 110 |      *  might look like this: | 
|---|
| 111 |      *@code | 
|---|
| 112 |      * | 
|---|
| 113 |      *    [March 01, 2004/June 07, 2004]   <-- closed range | 
|---|
| 114 |      *    [March 01, 2004/June 08, 2004)   <-- open range | 
|---|
| 115 |      * | 
|---|
| 116 |      *@endcode | 
|---|
| 117 |      */ | 
|---|
| 118 |     template<class period_type, class duration_type, class facet_type> | 
|---|
| 119 |     period_type get_period(stream_itr_type& sitr,  | 
|---|
| 120 |                            stream_itr_type& stream_end, | 
|---|
| 121 |                            std::ios_base& a_ios,  | 
|---|
| 122 |                            const period_type& p, | 
|---|
| 123 |                            const duration_type& dur_unit, | 
|---|
| 124 |                            const facet_type& facet) const  | 
|---|
| 125 |     { | 
|---|
| 126 |       // skip leading whitespace | 
|---|
| 127 |       while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }  | 
|---|
| 128 |       | 
|---|
| 129 |       typedef typename period_type::point_type point_type; | 
|---|
| 130 |       point_type p1(not_a_date_time), p2(not_a_date_time); | 
|---|
| 131 |  | 
|---|
| 132 |  | 
|---|
| 133 |       consume_delim(sitr, stream_end, delimiters[START]);       // start delim | 
|---|
| 134 |       facet.get(sitr, stream_end, a_ios, p1);                   // first point | 
|---|
| 135 |       consume_delim(sitr, stream_end, delimiters[SEPARATOR]);   // separator | 
|---|
| 136 |       facet.get(sitr, stream_end, a_ios, p2);                   // second point | 
|---|
| 137 |  | 
|---|
| 138 |       // period construction parameters are always open range [begin, end) | 
|---|
| 139 |       if (m_range_option == AS_CLOSED_RANGE) { | 
|---|
| 140 |         consume_delim(sitr, stream_end, delimiters[CLOSED_END]);// end delim | 
|---|
| 141 |         // add 1 duration unit to p2 to make range open | 
|---|
| 142 |         p2 += dur_unit; | 
|---|
| 143 |       } | 
|---|
| 144 |       else { | 
|---|
| 145 |         consume_delim(sitr, stream_end, delimiters[OPEN_END]);  // end delim | 
|---|
| 146 |       } | 
|---|
| 147 |      | 
|---|
| 148 |       return period_type(p1, p2); | 
|---|
| 149 |     } | 
|---|
| 150 |        | 
|---|
| 151 |   private: | 
|---|
| 152 |     collection_type delimiters;  | 
|---|
| 153 |     period_range_option m_range_option;     | 
|---|
| 154 |      | 
|---|
| 155 |     enum delim_ids { SEPARATOR, START, OPEN_END, CLOSED_END }; | 
|---|
| 156 |  | 
|---|
| 157 |     //! throws ios_base::failure if delimiter and parsed data do not match | 
|---|
| 158 |     void consume_delim(stream_itr_type& sitr, | 
|---|
| 159 |                        stream_itr_type& stream_end, | 
|---|
| 160 |                        const string_type& delim) const | 
|---|
| 161 |     { | 
|---|
| 162 |       /* string_parse_tree will not parse a string of punctuation characters  | 
|---|
| 163 |        * without knowing exactly how many characters to process | 
|---|
| 164 |        * Ex [2000. Will not parse out the '[' string without knowing  | 
|---|
| 165 |        * to process only one character. By using length of the delimiter  | 
|---|
| 166 |        * string we can safely iterate past it. */ | 
|---|
| 167 |       string_type s; | 
|---|
| 168 |       for(unsigned int i = 0; i < delim.length() && sitr != stream_end; ++i) { | 
|---|
| 169 |         s += *sitr; | 
|---|
| 170 |         ++sitr; | 
|---|
| 171 |       } | 
|---|
| 172 |       if(s != delim) { | 
|---|
| 173 |         throw std::ios_base::failure("Parse failed. Expected '" + convert_string_type<char_type,char>(delim) + "' but found '" + convert_string_type<char_type,char>(s) + "'"); | 
|---|
| 174 |       } | 
|---|
| 175 |     } | 
|---|
| 176 |   }; | 
|---|
| 177 |  | 
|---|
| 178 |   template <class date_type, class char_type>   | 
|---|
| 179 |   const typename period_parser<date_type, char_type>::char_type  | 
|---|
| 180 |   period_parser<date_type, char_type>::default_period_separator[2] = {'/'}; | 
|---|
| 181 |  | 
|---|
| 182 |   template <class date_type, class char_type>   | 
|---|
| 183 |   const typename period_parser<date_type, char_type>::char_type  | 
|---|
| 184 |   period_parser<date_type, char_type>::default_period_start_delimeter[2] = {'['}; | 
|---|
| 185 |  | 
|---|
| 186 |   template <class date_type, class char_type>   | 
|---|
| 187 |   const typename period_parser<date_type, char_type>::char_type  | 
|---|
| 188 |   period_parser<date_type, char_type>::default_period_open_range_end_delimeter[2] = {')'}; | 
|---|
| 189 |  | 
|---|
| 190 |   template <class date_type, class char_type>   | 
|---|
| 191 |   const typename period_parser<date_type, char_type>::char_type  | 
|---|
| 192 |   period_parser<date_type, char_type>::default_period_closed_range_end_delimeter[2] = {']'}; | 
|---|
| 193 |  | 
|---|
| 194 |  } } //namespace boost::date_time | 
|---|
| 195 |  | 
|---|
| 196 | #endif // DATETIME_PERIOD_PARSER_HPP___ | 
|---|