From 508c205d3596c2ef9862dc9226bec10dc37b444a Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Sat, 14 May 2016 08:46:21 +0200 Subject: [PATCH] Refactoring of iso_8601_grammar * formats 'hh:mm:ss' and ISO 8601 are merged into one grammar. * iso_8601_grammar is changed to static const. * iso_8601_grammar supports formats P[n]DT[n]H[n]M[n]S, P[n]W, and part of alternative PTHHMMSS and extended PTHH:MM:SS formats. --- CHANGELOG.md | 3 + .../extractor/extraction_helper_functions.hpp | 148 ++++++++++-------- include/util/iso_8601_duration_parser.hpp | 82 ---------- unit_tests/util/duration_parsing.cpp | 27 +++- 4 files changed, 112 insertions(+), 148 deletions(-) delete mode 100644 include/util/iso_8601_duration_parser.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index f5fe75f1f..b475c77d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ - new parameter `annotate` for `route` and `match` requests. Returns additional data about each coordinate along the selected/matched route line. + - Profile changes: + - duration parser now accepts P[n]DT[n]H[n]M[n]S, P[n]W, PTHHMMSS and PTHH:MM:SS ISO8601 formats. + # 5.1.0 Changes with regard to 5.0.0 diff --git a/include/extractor/extraction_helper_functions.hpp b/include/extractor/extraction_helper_functions.hpp index cbd5a64b5..b66936467 100644 --- a/include/extractor/extraction_helper_functions.hpp +++ b/include/extractor/extraction_helper_functions.hpp @@ -1,13 +1,8 @@ #ifndef EXTRACTION_HELPER_FUNCTIONS_HPP #define EXTRACTION_HELPER_FUNCTIONS_HPP -#include "util/cast.hpp" -#include "util/iso_8601_duration_parser.hpp" - -#include -#include #include -#include +#include #include #include @@ -17,83 +12,106 @@ namespace osrm namespace extractor { -inline bool simple_duration_is_valid(const std::string &s) +namespace detail { - boost::regex simple_format( - "((\\d|\\d\\d):(\\d|\\d\\d):(\\d|\\d\\d))|((\\d|\\d\\d):(\\d|\\d\\d))|(\\d|\\d\\d)", - boost::regex_constants::icase | boost::regex_constants::perl); - const bool simple_matched = regex_match(s, simple_format); +namespace qi = boost::spirit::qi; - if (simple_matched) - { - return true; - } - return false; -} - -inline bool iso_8601_duration_is_valid(const std::string &s) +template struct iso_8601_grammar : qi::grammar { - util::iso_8601_grammar iso_parser; - const bool result = boost::spirit::qi::parse(s.begin(), s.end(), iso_parser); + iso_8601_grammar() + : iso_8601_grammar::base_type(root) - // check if the was an error with the request - if (result && (0 != iso_parser.get_duration())) { - return true; + using qi::_1; + using qi::_a; + using qi::_b; + using qi::_c; + using qi::_pass; + using qi::_val; + using qi::eoi; + using qi::eps; + using qi::uint_; + using qi::char_; + + hh = uint2_p[_pass = bind([](unsigned x) { return x < 24; }, _1), _val = _1]; + mm = uint2_p[_pass = bind([](unsigned x) { return x < 60; }, _1), _val = _1]; + ss = uint2_p[_pass = bind([](unsigned x) { return x < 60; }, _1), _val = _1]; + + osm_time + = (uint_p[_a = _1] >> eoi) [_val = _a * 60] + | (uint_p[_a = _1] >> ':' >> uint_p[_b = _1] >> eoi) [_val = _a * 3600 + _b * 60] + | (uint_p[_a = _1] >> ':' >> uint_p[_b = _1] >> ':' >> uint_p[_c = _1] >> eoi) [_val = _a * 3600 + _b * 60 + _c] + ; + + alternative_time + = ('T' >> hh[_a = _1] >> mm[_b = _1] >> ss[_c = _1]) [_val = _a * 3600 + _b * 60 + _c] + ; + + extended_time + = ('T' >> hh[_a = _1] >> ':' >> mm[_b = _1] >> ':' >> ss[_c = _1]) [_val = _a * 3600 + _b * 60 + _c] + ; + + standard_time + = ('T' + >> -(uint_ >> char_("Hh"))[_a = _1] + >> -(uint_ >> char_("Mm"))[_b = _1] + >> -(uint_ >> char_("Ss"))[_c = _1]) [_val = _a * 3600 + _b * 60 + _c] + ; + + standard_date + = (uint_ >> char_("Dd")) [_val = _1 * 86400] + ; + + standard_week + = (uint_ >> char_("Ww")) [_val = _1 * 604800] + ; + + iso_period + = osm_time [_val = _1] + | ('P' >> standard_week >> eoi) [_val = _1] + | ('P' >> ( alternative_time[_a = 0, _b = _1] + | extended_time[_a = 0, _b = _1] + | (eps[_a = 0, _b = 0] >> -standard_date[_a = _1] >> -standard_time[_b = _1] ) ) + >> eoi) [_val = _a + _b] + ; + + root = iso_period; } - return false; + + qi::rule root; + qi::rule> iso_period; + qi::rule> osm_time, standard_time, alternative_time, extended_time; + qi::rule standard_date, standard_week; + qi::rule hh, mm, ss; + + qi::uint_parser uint_p; + qi::uint_parser uint2_p; +}; } inline bool durationIsValid(const std::string &s) { - return simple_duration_is_valid(s) || iso_8601_duration_is_valid(s); + static detail::iso_8601_grammar const iso_8601_grammar; + + std::string::const_iterator iter = s.begin(); + unsigned duration = 0; + boost::spirit::qi::parse(iter, s.end(), iso_8601_grammar, duration); + + return !s.empty() && iter == s.end(); } inline unsigned parseDuration(const std::string &s) { - if (simple_duration_is_valid(s)) - { - unsigned hours = 0; - unsigned minutes = 0; - unsigned seconds = 0; - boost::regex e( - "((\\d|\\d\\d):(\\d|\\d\\d):(\\d|\\d\\d))|((\\d|\\d\\d):(\\d|\\d\\d))|(\\d|\\d\\d)", - boost::regex_constants::icase | boost::regex_constants::perl); + static detail::iso_8601_grammar const iso_8601_grammar; - std::vector result; - boost::algorithm::split_regex(result, s, boost::regex(":")); - const bool matched = regex_match(s, e); - if (matched) - { - if (1 == result.size()) - { - minutes = std::stoul(result[0]); - } - if (2 == result.size()) - { - minutes = std::stoul(result[1]); - hours = std::stoul(result[0]); - } - if (3 == result.size()) - { - seconds = std::stoul(result[2]); - minutes = std::stoul(result[1]); - hours = std::stoul(result[0]); - } - return (3600 * hours + 60 * minutes + seconds); - } - } - else if (iso_8601_duration_is_valid(s)) - { - util::iso_8601_grammar iso_parser; - boost::spirit::qi::parse(s.begin(), s.end(), iso_parser); + std::string::const_iterator iter = s.begin(); + unsigned duration = 0; + boost::spirit::qi::parse(iter, s.end(), iso_8601_grammar, duration); - return iso_parser.get_duration(); - } - - return std::numeric_limits::max(); + return !s.empty() && iter == s.end() ? duration : std::numeric_limits::max(); } + } } diff --git a/include/util/iso_8601_duration_parser.hpp b/include/util/iso_8601_duration_parser.hpp deleted file mode 100644 index 91f9d2dc4..000000000 --- a/include/util/iso_8601_duration_parser.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef ISO_8601_DURATION_PARSER_HPP -#define ISO_8601_DURATION_PARSER_HPP - -#include -#include -#include - -namespace osrm -{ -namespace util -{ - -namespace qi = boost::spirit::qi; - -template struct iso_8601_grammar : qi::grammar -{ - iso_8601_grammar() - : iso_8601_grammar::base_type(iso_period), temp(0), hours(0), minutes(0), seconds(0) - { - iso_period = qi::lit('P') >> qi::lit('T') >> - ((value >> hour >> value >> minute >> value >> second) | - (value >> hour >> value >> minute) | (value >> hour >> value >> second) | - (value >> hour) | (value >> minute >> value >> second) | (value >> minute) | - (value >> second)); - - value = qi::uint_[boost::bind(&iso_8601_grammar::set_temp, this, ::_1)]; - second = (qi::lit('s') | - qi::lit('S'))[boost::bind(&iso_8601_grammar::set_seconds, this)]; - minute = (qi::lit('m') | - qi::lit('M'))[boost::bind(&iso_8601_grammar::set_minutes, this)]; - hour = (qi::lit('h') | - qi::lit('H'))[boost::bind(&iso_8601_grammar::set_hours, this)]; - } - - qi::rule iso_period; - qi::rule value, hour, minute, second; - - unsigned temp; - unsigned hours; - unsigned minutes; - unsigned seconds; - - void set_temp(unsigned number) { temp = number; } - - void set_hours() - { - if (temp < 24) - { - hours = temp; - } - } - - void set_minutes() - { - if (temp < 60) - { - minutes = temp; - } - } - - void set_seconds() - { - if (temp < 60) - { - seconds = temp; - } - } - - unsigned get_duration() const - { - unsigned temp = (3600 * hours + 60 * minutes + seconds); - if (temp == 0) - { - temp = std::numeric_limits::max(); - } - return temp; - } -}; -} -} - -#endif // ISO_8601_DURATION_PARSER_HPP diff --git a/unit_tests/util/duration_parsing.cpp b/unit_tests/util/duration_parsing.cpp index 72a5b6306..2e2eff64e 100644 --- a/unit_tests/util/duration_parsing.cpp +++ b/unit_tests/util/duration_parsing.cpp @@ -6,17 +6,32 @@ BOOST_AUTO_TEST_SUITE(durations_are_valid) using namespace osrm; -using namespace osrm::util; BOOST_AUTO_TEST_CASE(all_necessary_test) { + BOOST_CHECK_EQUAL(extractor::durationIsValid("0"), true); BOOST_CHECK_EQUAL(extractor::durationIsValid("00:01"), true); BOOST_CHECK_EQUAL(extractor::durationIsValid("00:01:01"), true); + BOOST_CHECK_EQUAL(extractor::durationIsValid("61"), true); + BOOST_CHECK_EQUAL(extractor::durationIsValid("24:01"), true); + BOOST_CHECK_EQUAL(extractor::durationIsValid("00:01:60"), true); BOOST_CHECK_EQUAL(extractor::durationIsValid("PT15M"), true); + + BOOST_CHECK_EQUAL(extractor::durationIsValid(""), false); + BOOST_CHECK_EQUAL(extractor::durationIsValid("PT15"), false); + BOOST_CHECK_EQUAL(extractor::durationIsValid("PT15A"), false); + BOOST_CHECK_EQUAL(extractor::durationIsValid("PT1H25:01"), false); + BOOST_CHECK_EQUAL(extractor::durationIsValid("PT12501"), false); + BOOST_CHECK_EQUAL(extractor::durationIsValid("PT0125:01"), false); + BOOST_CHECK_EQUAL(extractor::durationIsValid("PT016001"), false); + BOOST_CHECK_EQUAL(extractor::durationIsValid("PT240000"), false); + BOOST_CHECK_EQUAL(extractor::durationIsValid("PT24:00:00"), false); } BOOST_AUTO_TEST_CASE(common_durations_get_translated) { + BOOST_CHECK_EQUAL(extractor::parseDuration("00"), 0); + BOOST_CHECK_EQUAL(extractor::parseDuration("10"), 600); BOOST_CHECK_EQUAL(extractor::parseDuration("00:01"), 60); BOOST_CHECK_EQUAL(extractor::parseDuration("00:01:01"), 61); BOOST_CHECK_EQUAL(extractor::parseDuration("01:01"), 3660); @@ -29,12 +44,22 @@ BOOST_AUTO_TEST_CASE(common_durations_get_translated) BOOST_CHECK_EQUAL(extractor::parseDuration("PT15H"), 54000); BOOST_CHECK_EQUAL(extractor::parseDuration("PT1H15M"), 4500); BOOST_CHECK_EQUAL(extractor::parseDuration("PT1H15M1S"), 4501); + BOOST_CHECK_EQUAL(extractor::parseDuration("PT2H25M6S"), 8706); + BOOST_CHECK_EQUAL(extractor::parseDuration("P1DT2H15M1S"), 94501); + BOOST_CHECK_EQUAL(extractor::parseDuration("P4D"), 345600); + BOOST_CHECK_EQUAL(extractor::parseDuration("PT4H"), 14400); + BOOST_CHECK_EQUAL(extractor::parseDuration("PT71M"), 4260); + BOOST_CHECK_EQUAL(extractor::parseDuration("PT022506"), 8706); + BOOST_CHECK_EQUAL(extractor::parseDuration("PT02:25:06"), 8706); + BOOST_CHECK_EQUAL(extractor::parseDuration("P3W"), 1814400); } BOOST_AUTO_TEST_CASE(iso_8601_durations_case_insensitive) { BOOST_CHECK_EQUAL(extractor::parseDuration("PT15m"), 900); BOOST_CHECK_EQUAL(extractor::parseDuration("PT1h15m"), 4500); + BOOST_CHECK_EQUAL(extractor::parseDuration("PT1h15m42s"), 4542); + BOOST_CHECK_EQUAL(extractor::parseDuration("P2dT1h15m42s"), 177342); } BOOST_AUTO_TEST_SUITE_END()