Chris@16: Chris@16: #ifndef DATETIME_PERIOD_PARSER_HPP___ Chris@16: #define DATETIME_PERIOD_PARSER_HPP___ Chris@16: Chris@16: /* Copyright (c) 2002-2004 CrystalClear Software, Inc. Chris@16: * Use, modification and distribution is subject to the Chris@16: * Boost Software License, Version 1.0. (See accompanying Chris@16: * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) Chris@16: * Author: Jeff Garland, Bart Garst Chris@101: * $Date$ Chris@16: */ Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: Chris@16: namespace boost { namespace date_time { Chris@16: Chris@16: Chris@16: //! Not a facet, but a class used to specify and control period parsing Chris@16: /*! Provides settings for the following: Chris@16: * - period_separator -- default '/' Chris@16: * - period_open_start_delimeter -- default '[' Chris@16: * - period_open_range_end_delimeter -- default ')' Chris@16: * - period_closed_range_end_delimeter -- default ']' Chris@16: * - display_as_open_range, display_as_closed_range -- default closed_range Chris@16: * Chris@16: * For a typical date_period, the contents of the input stream would be Chris@16: *@code Chris@16: * [2004-Jan-04/2004-Feb-01] Chris@16: *@endcode Chris@16: * where the date format is controlled by the date facet Chris@16: */ Chris@16: template Chris@16: class period_parser { Chris@16: public: Chris@16: typedef std::basic_string string_type; Chris@16: typedef CharT char_type; Chris@16: //typedef typename std::basic_string::const_iterator const_itr_type; Chris@16: typedef std::istreambuf_iterator stream_itr_type; Chris@16: typedef string_parse_tree parse_tree_type; Chris@16: typedef typename parse_tree_type::parse_match_result_type match_results; Chris@16: typedef std::vector > collection_type; Chris@16: Chris@16: static const char_type default_period_separator[2]; Chris@16: static const char_type default_period_start_delimeter[2]; Chris@16: static const char_type default_period_open_range_end_delimeter[2]; Chris@16: static const char_type default_period_closed_range_end_delimeter[2]; Chris@16: Chris@16: enum period_range_option { AS_OPEN_RANGE, AS_CLOSED_RANGE }; Chris@16: Chris@16: //! Constructor that sets up period parser options Chris@16: period_parser(period_range_option range_opt = AS_CLOSED_RANGE, Chris@16: const char_type* const period_separator = default_period_separator, Chris@16: const char_type* const period_start_delimeter = default_period_start_delimeter, Chris@16: const char_type* const period_open_range_end_delimeter = default_period_open_range_end_delimeter, Chris@16: const char_type* const period_closed_range_end_delimeter = default_period_closed_range_end_delimeter) Chris@16: : m_range_option(range_opt) Chris@16: { Chris@16: delimiters.push_back(string_type(period_separator)); Chris@16: delimiters.push_back(string_type(period_start_delimeter)); Chris@16: delimiters.push_back(string_type(period_open_range_end_delimeter)); Chris@16: delimiters.push_back(string_type(period_closed_range_end_delimeter)); Chris@16: } Chris@16: Chris@16: period_parser(const period_parser& p_parser) Chris@16: { Chris@16: this->delimiters = p_parser.delimiters; Chris@16: this->m_range_option = p_parser.m_range_option; Chris@16: } Chris@16: Chris@16: period_range_option range_option() const Chris@16: { Chris@16: return m_range_option; Chris@16: } Chris@16: void range_option(period_range_option option) Chris@16: { Chris@16: m_range_option = option; Chris@16: } Chris@16: collection_type delimiter_strings() const Chris@16: { Chris@16: return delimiters; Chris@16: } Chris@16: void delimiter_strings(const string_type& separator, Chris@16: const string_type& start_delim, Chris@16: const string_type& open_end_delim, Chris@16: const string_type& closed_end_delim) Chris@16: { Chris@16: delimiters.clear(); Chris@16: delimiters.push_back(separator); Chris@16: delimiters.push_back(start_delim); Chris@16: delimiters.push_back(open_end_delim); Chris@16: delimiters.push_back(closed_end_delim); Chris@16: } Chris@16: Chris@16: //! Generic code to parse a period -- no matter the period type. Chris@16: /*! This generic code will parse any period using a facet to Chris@16: * to get the 'elements'. For example, in the case of a date_period Chris@16: * the elements will be instances of a date which will be parsed Chris@16: * according the to setup in the passed facet parameter. Chris@16: * Chris@16: * The steps for parsing a period are always the same: Chris@16: * - consume the start delimiter Chris@16: * - get start element Chris@16: * - consume the separator Chris@16: * - get either last or end element depending on range settings Chris@16: * - consume the end delimeter depending on range settings Chris@16: * Chris@16: * Thus for a typical date period the contents of the input stream Chris@16: * might look like this: Chris@16: *@code Chris@16: * Chris@16: * [March 01, 2004/June 07, 2004] <-- closed range Chris@16: * [March 01, 2004/June 08, 2004) <-- open range Chris@16: * Chris@16: *@endcode Chris@16: */ Chris@16: template Chris@16: period_type get_period(stream_itr_type& sitr, Chris@16: stream_itr_type& stream_end, Chris@16: std::ios_base& a_ios, Chris@16: const period_type& /* p */, Chris@16: const duration_type& dur_unit, Chris@16: const facet_type& facet) const Chris@16: { Chris@16: // skip leading whitespace Chris@16: while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; } Chris@16: Chris@16: typedef typename period_type::point_type point_type; Chris@16: point_type p1(not_a_date_time), p2(not_a_date_time); Chris@16: Chris@16: Chris@16: consume_delim(sitr, stream_end, delimiters[START]); // start delim Chris@16: facet.get(sitr, stream_end, a_ios, p1); // first point Chris@16: consume_delim(sitr, stream_end, delimiters[SEPARATOR]); // separator Chris@16: facet.get(sitr, stream_end, a_ios, p2); // second point Chris@16: Chris@16: // period construction parameters are always open range [begin, end) Chris@16: if (m_range_option == AS_CLOSED_RANGE) { Chris@16: consume_delim(sitr, stream_end, delimiters[CLOSED_END]);// end delim Chris@16: // add 1 duration unit to p2 to make range open Chris@16: p2 += dur_unit; Chris@16: } Chris@16: else { Chris@16: consume_delim(sitr, stream_end, delimiters[OPEN_END]); // end delim Chris@16: } Chris@16: Chris@16: return period_type(p1, p2); Chris@16: } Chris@16: Chris@16: private: Chris@16: collection_type delimiters; Chris@16: period_range_option m_range_option; Chris@16: Chris@16: enum delim_ids { SEPARATOR, START, OPEN_END, CLOSED_END }; Chris@16: Chris@16: //! throws ios_base::failure if delimiter and parsed data do not match Chris@16: void consume_delim(stream_itr_type& sitr, Chris@16: stream_itr_type& stream_end, Chris@16: const string_type& delim) const Chris@16: { Chris@16: /* string_parse_tree will not parse a string of punctuation characters Chris@16: * without knowing exactly how many characters to process Chris@16: * Ex [2000. Will not parse out the '[' string without knowing Chris@16: * to process only one character. By using length of the delimiter Chris@16: * string we can safely iterate past it. */ Chris@16: string_type s; Chris@16: for(unsigned int i = 0; i < delim.length() && sitr != stream_end; ++i) { Chris@16: s += *sitr; Chris@16: ++sitr; Chris@16: } Chris@16: if(s != delim) { Chris@16: boost::throw_exception(std::ios_base::failure("Parse failed. Expected '" Chris@16: + convert_string_type(delim) + "' but found '" + convert_string_type(s) + "'")); Chris@16: } Chris@16: } Chris@16: }; Chris@16: Chris@16: template Chris@16: const typename period_parser::char_type Chris@16: period_parser::default_period_separator[2] = {'/'}; Chris@16: Chris@16: template Chris@16: const typename period_parser::char_type Chris@16: period_parser::default_period_start_delimeter[2] = {'['}; Chris@16: Chris@16: template Chris@16: const typename period_parser::char_type Chris@16: period_parser::default_period_open_range_end_delimeter[2] = {')'}; Chris@16: Chris@16: template Chris@16: const typename period_parser::char_type Chris@16: period_parser::default_period_closed_range_end_delimeter[2] = {']'}; Chris@16: Chris@16: } } //namespace boost::date_time Chris@16: Chris@16: #endif // DATETIME_PERIOD_PARSER_HPP___