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,23 +38,42 @@ 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)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (simple_duration_is_valid(s))
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        unsigned hours = 0;
 | 
					        unsigned hours = 0;
 | 
				
			||||||
        unsigned minutes = 0;
 | 
					        unsigned minutes = 0;
 | 
				
			||||||
@ -84,6 +104,15 @@ unsigned parseDuration(const std::string &s)
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            return 10 * (3600 * hours + 60 * minutes + seconds);
 | 
					            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