summaryrefslogtreecommitdiffstats
blob: f11eb42d52e81ea45127ceb92b6d6c50d07db9f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330

#ifndef DATE_TIME_DATE_GENERATOR_PARSER_HPP__
#define DATE_TIME_DATE_GENERATOR_PARSER_HPP__

/* Copyright (c) 2005 CrystalClear Software, Inc.
 * Use, modification and distribution is subject to the 
 * Boost Software License, Version 1.0. (See accompanying
 * file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 * Author: Jeff Garland, Bart Garst
 * $Date: 2008-11-12 14:37:53 -0500 (Wed, 12 Nov 2008) $
 */

#include <string>
#include <vector>
#include <iterator> // istreambuf_iterator
#include <boost/throw_exception.hpp>
#include <boost/date_time/compiler_config.hpp>
#include <boost/date_time/string_parse_tree.hpp>
#include <boost/date_time/date_generators.hpp>
#include <boost/date_time/format_date_parser.hpp>

namespace boost { namespace date_time {

  //! Class for date_generator parsing 
  /*! The elements of a date_generator "phrase" are parsed from the input stream in a 
   *  particular order. All elements are required and the order in which they appear 
   *  cannot change, however, the elements themselves can be changed. The default 
   *  elements and their order are as follows:
   *
   * - partial_date                     => "dd Month"
   * - nth_day_of_the_week_in_month     => "nth weekday of month"
   * - first_day_of_the_week_in_month   => "first weekday of month"
   * - last_day_of_the_week_in_month    => "last weekday of month"
   * - first_day_of_the_week_after      => "weekday after"
   * - first_day_of_the_week_before     => "weekday before"
   *
   * Weekday and Month names and formats are handled via the date_input_facet. 
   *
   */
  template<class date_type, typename charT>
  class date_generator_parser
  {
   public:
    typedef std::basic_string<charT>        string_type;
    typedef std::istreambuf_iterator<charT> stream_itr_type;

    typedef typename date_type::month_type       month_type;
    typedef typename date_type::day_of_week_type day_of_week_type;
    typedef typename date_type::day_type         day_type;

    typedef string_parse_tree<charT>                          parse_tree_type;
    typedef typename parse_tree_type::parse_match_result_type match_results;
    typedef std::vector<std::basic_string<charT> >            collection_type;

    typedef partial_date<date_type>          partial_date_type;
    typedef nth_kday_of_month<date_type>     nth_kday_type;
    typedef first_kday_of_month<date_type>   first_kday_type;
    typedef last_kday_of_month<date_type>    last_kday_type;
    typedef first_kday_after<date_type>      kday_after_type;
    typedef first_kday_before<date_type>     kday_before_type;

    typedef charT char_type;
    static const char_type first_string[6];
    static const char_type second_string[7];
    static const char_type third_string[6];
    static const char_type fourth_string[7];
    static const char_type fifth_string[6];
    static const char_type last_string[5];
    static const char_type before_string[8];
    static const char_type after_string[6];
    static const char_type of_string[3];

    enum phrase_elements {first=0, second, third, fourth, fifth, last,
                          before, after, of, number_of_phrase_elements};

    //! Creates a date_generator_parser with the default set of "element_strings"
    date_generator_parser()
    {
      element_strings(string_type(first_string),
                      string_type(second_string),
                      string_type(third_string),
                      string_type(fourth_string),
                      string_type(fifth_string),
                      string_type(last_string),
                      string_type(before_string),
                      string_type(after_string),
                      string_type(of_string));
    }

    //! Creates a date_generator_parser using a user defined set of element strings
    date_generator_parser(const string_type& first_str,
                          const string_type& second_str,
                          const string_type& third_str,
                          const string_type& fourth_str,
                          const string_type& fifth_str,
                          const string_type& last_str,
                          const string_type& before_str,
                          const string_type& after_str,
                          const string_type& of_str)
    {
      element_strings(first_str, second_str, third_str, fourth_str, fifth_str,
                      last_str, before_str, after_str, of_str);
    }

    //! Replace strings that determine nth week for generator
    void element_strings(const string_type& first_str,
                         const string_type& second_str,
                         const string_type& third_str,
                         const string_type& fourth_str,
                         const string_type& fifth_str,
                         const string_type& last_str,
                         const string_type& before_str,
                         const string_type& after_str,
                         const string_type& of_str)
    {
      collection_type phrases;
      phrases.push_back(first_str);
      phrases.push_back(second_str);
      phrases.push_back(third_str);
      phrases.push_back(fourth_str);
      phrases.push_back(fifth_str);
      phrases.push_back(last_str);
      phrases.push_back(before_str);
      phrases.push_back(after_str);
      phrases.push_back(of_str);
      m_element_strings = parse_tree_type(phrases, this->first); // enum first
    }

    void element_strings(const collection_type& col)
    {
      m_element_strings = parse_tree_type(col, this->first); // enum first
    }

    //! returns partial_date parsed from stream
    template<class facet_type>
    partial_date_type
    get_partial_date_type(stream_itr_type& sitr,
                          stream_itr_type& stream_end,
                          std::ios_base& a_ios,
                          const facet_type& facet) const
    {
      // skip leading whitespace
      while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }

      day_type d(1);
      month_type m(1);
      facet.get(sitr, stream_end, a_ios, d);
      facet.get(sitr, stream_end, a_ios, m);

      return partial_date_type(d,m);
    }

    //! returns nth_kday_of_week parsed from stream
    template<class facet_type>
    nth_kday_type
    get_nth_kday_type(stream_itr_type& sitr,
                      stream_itr_type& stream_end,
                      std::ios_base& a_ios,
                      const facet_type& facet) const
    {
      // skip leading whitespace
      while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }
 
      typename nth_kday_type::week_num wn;
      day_of_week_type wd(0); // no default constructor
      month_type m(1);        // no default constructor

      match_results mr = m_element_strings.match(sitr, stream_end);
      switch(mr.current_match) {
        case first  : { wn = nth_kday_type::first; break; }
        case second : { wn = nth_kday_type::second; break; }
        case third  : { wn = nth_kday_type::third; break; }
        case fourth : { wn = nth_kday_type::fourth; break; }
        case fifth  : { wn = nth_kday_type::fifth; break; }
        default:
        {
          boost::throw_exception(std::ios_base::failure("Parse failed. No match found for '" + mr.cache + "'"));
          BOOST_DATE_TIME_UNREACHABLE_EXPRESSION(wn = nth_kday_type::first);
        }
      }                                         // week num
      facet.get(sitr, stream_end, a_ios, wd);   // day_of_week
      extract_element(sitr, stream_end, of);    // "of" element
      facet.get(sitr, stream_end, a_ios, m);    // month

      return nth_kday_type(wn, wd, m);
    }

    //! returns first_kday_of_week parsed from stream
    template<class facet_type>
    first_kday_type
    get_first_kday_type(stream_itr_type& sitr,
                        stream_itr_type& stream_end,
                        std::ios_base& a_ios,
                        const facet_type& facet) const
    {
      // skip leading whitespace
      while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }

      day_of_week_type wd(0); // no default constructor
      month_type m(1);        // no default constructor

      extract_element(sitr, stream_end, first); // "first" element
      facet.get(sitr, stream_end, a_ios, wd);   // day_of_week
      extract_element(sitr, stream_end, of);    // "of" element
      facet.get(sitr, stream_end, a_ios, m);    // month


      return first_kday_type(wd, m);
    }

    //! returns last_kday_of_week parsed from stream
    template<class facet_type>
    last_kday_type
    get_last_kday_type(stream_itr_type& sitr,
                       stream_itr_type& stream_end,
                       std::ios_base& a_ios,
                       const facet_type& facet) const
    {
      // skip leading whitespace
      while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }

      day_of_week_type wd(0); // no default constructor
      month_type m(1);        // no default constructor
 
      extract_element(sitr, stream_end, last); // "last" element
      facet.get(sitr, stream_end, a_ios, wd);  // day_of_week
      extract_element(sitr, stream_end, of);   // "of" element
      facet.get(sitr, stream_end, a_ios, m);   // month


      return last_kday_type(wd, m);
    }

    //! returns first_kday_of_week parsed from stream
    template<class facet_type>
    kday_before_type
    get_kday_before_type(stream_itr_type& sitr,
                         stream_itr_type& stream_end,
                         std::ios_base& a_ios,
                         const facet_type& facet) const
    {
      // skip leading whitespace
      while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }

      day_of_week_type wd(0); // no default constructor

      facet.get(sitr, stream_end, a_ios, wd);   // day_of_week
      extract_element(sitr, stream_end, before);// "before" element

      return kday_before_type(wd);
    }

    //! returns first_kday_of_week parsed from stream
    template<class facet_type>
    kday_after_type
    get_kday_after_type(stream_itr_type& sitr,
                        stream_itr_type& stream_end,
                        std::ios_base& a_ios,
                        const facet_type& facet) const
    {
      // skip leading whitespace
      while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }

      day_of_week_type wd(0); // no default constructor

      facet.get(sitr, stream_end, a_ios, wd);   // day_of_week
      extract_element(sitr, stream_end, after); // "after" element

      return kday_after_type(wd);
    }

   private:
    parse_tree_type m_element_strings;

    //! Extracts phrase element from input. Throws ios_base::failure on error.
    void extract_element(stream_itr_type& sitr,
                         stream_itr_type& stream_end,
                         typename date_generator_parser::phrase_elements ele) const
    {
      // skip leading whitespace
      while(std::isspace(*sitr) && sitr != stream_end) { ++sitr; }
      match_results mr = m_element_strings.match(sitr, stream_end);
      if(mr.current_match != ele) {
        boost::throw_exception(std::ios_base::failure("Parse failed. No match found for '" + mr.cache + "'"));
      }
    }

  };

  template<class date_type, class CharT>
  const typename date_generator_parser<date_type, CharT>::char_type
  date_generator_parser<date_type, CharT>::first_string[6] =
    {'f','i','r','s','t'};
  template<class date_type, class CharT>
  const typename date_generator_parser<date_type, CharT>::char_type
  date_generator_parser<date_type, CharT>::second_string[7] =
    {'s','e','c','o','n','d'};
  template<class date_type, class CharT>
  const typename date_generator_parser<date_type, CharT>::char_type
  date_generator_parser<date_type, CharT>::third_string[6] =
    {'t','h','i','r','d'};
  template<class date_type, class CharT>
  const typename date_generator_parser<date_type, CharT>::char_type
  date_generator_parser<date_type, CharT>::fourth_string[7] =
    {'f','o','u','r','t','h'};
  template<class date_type, class CharT>
  const typename date_generator_parser<date_type, CharT>::char_type
  date_generator_parser<date_type, CharT>::fifth_string[6] =
    {'f','i','f','t','h'};
  template<class date_type, class CharT>
  const typename date_generator_parser<date_type, CharT>::char_type
  date_generator_parser<date_type, CharT>::last_string[5] =
    {'l','a','s','t'};
  template<class date_type, class CharT>
  const typename date_generator_parser<date_type, CharT>::char_type
  date_generator_parser<date_type, CharT>::before_string[8] =
    {'b','e','f','o','r','e'};
  template<class date_type, class CharT>
  const typename date_generator_parser<date_type, CharT>::char_type
  date_generator_parser<date_type, CharT>::after_string[6] =
    {'a','f','t','e','r'};
  template<class date_type, class CharT>
  const typename date_generator_parser<date_type, CharT>::char_type
  date_generator_parser<date_type, CharT>::of_string[3] =
    {'o','f'};

} } //namespace

#endif // DATE_TIME_DATE_GENERATOR_PARSER_HPP__