implement ISO 8601 durations parsing, cf. #1399
This commit is contained in:
parent
73a2a938b4
commit
7e00a86bb4
@ -29,6 +29,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#define EXTRACTION_HELPER_FUNCTIONS_HPP
|
#define EXTRACTION_HELPER_FUNCTIONS_HPP
|
||||||
|
|
||||||
#include "../util/cast.hpp"
|
#include "../util/cast.hpp"
|
||||||
|
#include "../util/iso_8601_duration_parser.hpp"
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/algorithm/string_regex.hpp>
|
#include <boost/algorithm/string_regex.hpp>
|
||||||
@ -37,53 +38,81 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
namespace qi = boost::spirit::qi;
|
bool simple_duration_is_valid(const std::string &s)
|
||||||
|
|
||||||
// TODO: Move into LUA
|
|
||||||
|
|
||||||
bool durationIsValid(const std::string &s)
|
|
||||||
{
|
{
|
||||||
boost::regex e(
|
boost::regex simple_format(
|
||||||
"((\\d|\\d\\d):(\\d|\\d\\d):(\\d|\\d\\d))|((\\d|\\d\\d):(\\d|\\d\\d))|(\\d|\\d\\d)",
|
"((\\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);
|
boost::regex_constants::icase | boost::regex_constants::perl);
|
||||||
|
|
||||||
std::vector<std::string> result;
|
const bool simple_matched = regex_match(s, simple_format);
|
||||||
boost::algorithm::split_regex(result, s, boost::regex(":"));
|
|
||||||
const bool matched = regex_match(s, e);
|
if (simple_matched)
|
||||||
return matched;
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iso_8601_duration_is_valid(const std::string &s)
|
||||||
|
{
|
||||||
|
iso_8601_grammar<std::string::const_iterator> iso_parser;
|
||||||
|
const bool result = qi::parse(s.begin(), s.end(), iso_parser);
|
||||||
|
|
||||||
|
// check if the was an error with the request
|
||||||
|
if (result && (0 != iso_parser.get_duration()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool durationIsValid(const std::string &s)
|
||||||
|
{
|
||||||
|
return simple_duration_is_valid(s) || iso_8601_duration_is_valid(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned parseDuration(const std::string &s)
|
unsigned parseDuration(const std::string &s)
|
||||||
{
|
{
|
||||||
unsigned hours = 0;
|
if (simple_duration_is_valid(s))
|
||||||
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);
|
|
||||||
|
|
||||||
std::vector<std::string> result;
|
|
||||||
boost::algorithm::split_regex(result, s, boost::regex(":"));
|
|
||||||
const bool matched = regex_match(s, e);
|
|
||||||
if (matched)
|
|
||||||
{
|
{
|
||||||
if (1 == result.size())
|
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);
|
||||||
|
|
||||||
|
std::vector<std::string> result;
|
||||||
|
boost::algorithm::split_regex(result, s, boost::regex(":"));
|
||||||
|
const bool matched = regex_match(s, e);
|
||||||
|
if (matched)
|
||||||
{
|
{
|
||||||
minutes = cast::string_to_int(result[0]);
|
if (1 == result.size())
|
||||||
|
{
|
||||||
|
minutes = cast::string_to_int(result[0]);
|
||||||
|
}
|
||||||
|
if (2 == result.size())
|
||||||
|
{
|
||||||
|
minutes = cast::string_to_int(result[1]);
|
||||||
|
hours = cast::string_to_int(result[0]);
|
||||||
|
}
|
||||||
|
if (3 == result.size())
|
||||||
|
{
|
||||||
|
seconds = cast::string_to_int(result[2]);
|
||||||
|
minutes = cast::string_to_int(result[1]);
|
||||||
|
hours = cast::string_to_int(result[0]);
|
||||||
|
}
|
||||||
|
return 10 * (3600 * hours + 60 * minutes + seconds);
|
||||||
}
|
}
|
||||||
if (2 == result.size())
|
|
||||||
{
|
|
||||||
minutes = cast::string_to_int(result[1]);
|
|
||||||
hours = cast::string_to_int(result[0]);
|
|
||||||
}
|
|
||||||
if (3 == result.size())
|
|
||||||
{
|
|
||||||
seconds = cast::string_to_int(result[2]);
|
|
||||||
minutes = cast::string_to_int(result[1]);
|
|
||||||
hours = cast::string_to_int(result[0]);
|
|
||||||
}
|
|
||||||
return 10 * (3600 * hours + 60 * minutes + seconds);
|
|
||||||
}
|
}
|
||||||
|
else if (iso_8601_duration_is_valid(s))
|
||||||
|
{
|
||||||
|
iso_8601_grammar<std::string::const_iterator> iso_parser;
|
||||||
|
qi::parse(s.begin(), s.end(), iso_parser);
|
||||||
|
|
||||||
|
return iso_parser.get_duration();
|
||||||
|
}
|
||||||
|
|
||||||
return std::numeric_limits<unsigned>::max();
|
return std::numeric_limits<unsigned>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ Feature: Car - Handle ferry routes
|
|||||||
| c | f | cde,efg | 2,1 |
|
| c | f | cde,efg | 2,1 |
|
||||||
| c | g | cde,efg | 2,1 |
|
| c | g | cde,efg | 2,1 |
|
||||||
|
|
||||||
Scenario: Car - Properly handle durations
|
Scenario: Car - Properly handle simple durations
|
||||||
Given the node map
|
Given the node map
|
||||||
| a | b | c | | |
|
| a | b | c | | |
|
||||||
| | | d | | |
|
| | | d | | |
|
||||||
@ -45,3 +45,22 @@ Feature: Car - Handle ferry routes
|
|||||||
| b | f | abc,cde,efg | 1,2,1 | 20 km/h |
|
| b | f | abc,cde,efg | 1,2,1 | 20 km/h |
|
||||||
| c | e | cde | 2 | 12 km/h |
|
| c | e | cde | 2 | 12 km/h |
|
||||||
| e | c | cde | 2 | 12 km/h |
|
| e | c | cde | 2 | 12 km/h |
|
||||||
|
|
||||||
|
Scenario: Car - Properly handle ISO 8601 durations
|
||||||
|
Given the node map
|
||||||
|
| a | b | c | | |
|
||||||
|
| | | d | | |
|
||||||
|
| | | e | f | g |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | highway | route | duration |
|
||||||
|
| abc | primary | | |
|
||||||
|
| cde | | ferry | PT1M |
|
||||||
|
| efg | primary | | |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| from | to | route | modes | speed |
|
||||||
|
| a | g | abc,cde,efg | 1,2,1 | 26 km/h |
|
||||||
|
| b | f | abc,cde,efg | 1,2,1 | 20 km/h |
|
||||||
|
| c | e | cde | 2 | 12 km/h |
|
||||||
|
| e | c | cde | 2 | 12 km/h |
|
||||||
|
64
unit_tests/algorithms/duration_parsing.cpp
Normal file
64
unit_tests/algorithms/duration_parsing.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2015, Project OSRM contributors
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright notice, this list
|
||||||
|
of conditions and the following disclaimer.
|
||||||
|
Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../../extractor/extraction_helper_functions.hpp"
|
||||||
|
|
||||||
|
#include <boost/test/unit_test.hpp>
|
||||||
|
#include <boost/test/test_case_template.hpp>
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(durations_are_valid)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(all_necessary_test)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(durationIsValid("00:01"), true);
|
||||||
|
BOOST_CHECK_EQUAL(durationIsValid("00:01:01"), true);
|
||||||
|
BOOST_CHECK_EQUAL(durationIsValid("PT15M"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(common_durations_get_translated)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("00:01"), 600);
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("00:01:01"), 610);
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("01:01"), 36600);
|
||||||
|
|
||||||
|
// check all combinations of iso duration tokens
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("PT1M1S"), 610);
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("PT1H1S"), 36010);
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("PT15M"), 9000);
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("PT15S"), 150);
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("PT15H"), 540000);
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("PT1H15M"), 45000);
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("PT1H15M1S"), 45010);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(iso_8601_durations_case_insensitive)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("PT15m"), 9000);
|
||||||
|
BOOST_CHECK_EQUAL(parseDuration("PT1h15m"), 45000);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
102
util/iso_8601_duration_parser.hpp
Normal file
102
util/iso_8601_duration_parser.hpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2015, Project OSRM contributors
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright notice, this list
|
||||||
|
of conditions and the following disclaimer.
|
||||||
|
Redistributions in binary form must reproduce the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer in the documentation and/or
|
||||||
|
other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ISO_8601_DURATION_PARSER_HPP
|
||||||
|
#define ISO_8601_DURATION_PARSER_HPP
|
||||||
|
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/spirit/include/qi.hpp>
|
||||||
|
#include <boost/spirit/include/qi_action.hpp>
|
||||||
|
|
||||||
|
namespace qi = boost::spirit::qi;
|
||||||
|
|
||||||
|
template <typename Iterator> struct iso_8601_grammar : qi::grammar<Iterator>
|
||||||
|
{
|
||||||
|
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<Iterator>::set_temp, this, ::_1)];
|
||||||
|
second = (qi::lit('s') |
|
||||||
|
qi::lit('S'))[boost::bind(&iso_8601_grammar<Iterator>::set_seconds, this)];
|
||||||
|
minute = (qi::lit('m') |
|
||||||
|
qi::lit('M'))[boost::bind(&iso_8601_grammar<Iterator>::set_minutes, this)];
|
||||||
|
hour = (qi::lit('h') |
|
||||||
|
qi::lit('H'))[boost::bind(&iso_8601_grammar<Iterator>::set_hours, this)];
|
||||||
|
}
|
||||||
|
|
||||||
|
qi::rule<Iterator> iso_period;
|
||||||
|
qi::rule<Iterator, std::string()> 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 = 10 * (3600 * hours + 60 * minutes + seconds);
|
||||||
|
if (temp == 0)
|
||||||
|
{
|
||||||
|
temp = std::numeric_limits<unsigned>::max();
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ISO_8601_DURATION_PARSER_HPP
|
Loading…
Reference in New Issue
Block a user