period_parser.hpp 7.8 KB

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