period_parser.hpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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 <ios>
  11. #include <string>
  12. #include <vector>
  13. #include <iterator>
  14. #include <boost/throw_exception.hpp>
  15. #include <boost/date_time/special_defs.hpp>
  16. #include <boost/date_time/string_parse_tree.hpp>
  17. #include <boost/date_time/string_convert.hpp>
  18. namespace boost { namespace date_time {
  19. //! Not a facet, but a class used to specify and control period parsing
  20. /*! Provides settings for the following:
  21. * - period_separator -- default '/'
  22. * - period_open_start_delimeter -- default '['
  23. * - period_open_range_end_delimeter -- default ')'
  24. * - period_closed_range_end_delimeter -- default ']'
  25. * - display_as_open_range, display_as_closed_range -- default closed_range
  26. *
  27. * For a typical date_period, the contents of the input stream would be
  28. *@code
  29. * [2004-Jan-04/2004-Feb-01]
  30. *@endcode
  31. * where the date format is controlled by the date facet
  32. */
  33. template<class date_type, typename CharT>
  34. class period_parser {
  35. public:
  36. typedef std::basic_string<CharT> string_type;
  37. typedef CharT char_type;
  38. //typedef typename std::basic_string<char_type>::const_iterator const_itr_type;
  39. typedef std::istreambuf_iterator<CharT> stream_itr_type;
  40. typedef string_parse_tree<CharT> parse_tree_type;
  41. typedef typename parse_tree_type::parse_match_result_type match_results;
  42. typedef std::vector<std::basic_string<CharT> > collection_type;
  43. static const char_type default_period_separator[2];
  44. static const char_type default_period_start_delimeter[2];
  45. static const char_type default_period_open_range_end_delimeter[2];
  46. static const char_type default_period_closed_range_end_delimeter[2];
  47. enum period_range_option { AS_OPEN_RANGE, AS_CLOSED_RANGE };
  48. //! Constructor that sets up period parser options
  49. period_parser(period_range_option range_opt = AS_CLOSED_RANGE,
  50. const char_type* const period_separator = default_period_separator,
  51. const char_type* const period_start_delimeter = default_period_start_delimeter,
  52. const char_type* const period_open_range_end_delimeter = default_period_open_range_end_delimeter,
  53. const char_type* const period_closed_range_end_delimeter = default_period_closed_range_end_delimeter)
  54. : m_range_option(range_opt)
  55. {
  56. delimiters.push_back(string_type(period_separator));
  57. delimiters.push_back(string_type(period_start_delimeter));
  58. delimiters.push_back(string_type(period_open_range_end_delimeter));
  59. delimiters.push_back(string_type(period_closed_range_end_delimeter));
  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___