forked from organicmaps/organicmaps
[coding] Add Uri class.
This commit is contained in:
parent
9ae450222c
commit
f93a18c9bd
5 changed files with 225 additions and 0 deletions
|
@ -30,6 +30,7 @@ SOURCES += \
|
|||
reader_writer_ops.cpp \
|
||||
blob_indexer.cpp \
|
||||
blob_storage.cpp \
|
||||
uri.cpp \
|
||||
|
||||
HEADERS += \
|
||||
internal/xmlparser.h \
|
||||
|
@ -87,3 +88,4 @@ HEADERS += \
|
|||
reader_wrapper.hpp \
|
||||
blob_indexer.hpp \
|
||||
blob_storage.hpp \
|
||||
uri.hpp \
|
||||
|
|
|
@ -39,6 +39,7 @@ SOURCES += ../../testing/testingmain.cpp \
|
|||
trie_test.cpp \
|
||||
reader_writer_ops_test.cpp \
|
||||
blob_storage_test.cpp \
|
||||
uri_test.cpp \
|
||||
|
||||
HEADERS += \
|
||||
reader_test.hpp \
|
||||
|
|
117
coding/coding_tests/uri_test.cpp
Normal file
117
coding/coding_tests/uri_test.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include "../../testing/testing.hpp"
|
||||
#include "../uri.hpp"
|
||||
#include "../../base/macros.hpp"
|
||||
#include "../../std/bind.hpp"
|
||||
#include "../../std/queue.hpp"
|
||||
#include "../../std/utility.hpp"
|
||||
|
||||
using url_scheme::Uri;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class TestUri
|
||||
{
|
||||
public:
|
||||
TestUri(string const & uri) { m_uri = uri; }
|
||||
TestUri & Scheme(string const &scheme) { m_scheme = scheme; return *this; }
|
||||
TestUri & Path(string const & path) { m_path = path; return *this; }
|
||||
TestUri & KV(string const & key, string const & value)
|
||||
{
|
||||
m_keyValuePairs.push(make_pair(key, value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
~TestUri()
|
||||
{
|
||||
Uri uri(&m_uri[0], m_uri.size());
|
||||
TEST_EQUAL(uri.GetScheme(), m_scheme, ());
|
||||
TEST_EQUAL(uri.GetPath(), m_path, ());
|
||||
TEST(!m_scheme.empty() || !uri.IsValid(), ("Scheme is empty if and only if uri is invalid!"));
|
||||
uri.ForEachKeyValue(bind(&TestUri::AddTestValue, this, _1, _2));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void AddTestValue(string const & key, string const & value)
|
||||
{
|
||||
TEST(!m_keyValuePairs.empty(), ("Check that key/value pair is expected"));
|
||||
TEST_EQUAL(m_keyValuePairs.front().first, key, ());
|
||||
TEST_EQUAL(m_keyValuePairs.front().second, value, ());
|
||||
m_keyValuePairs.pop();
|
||||
}
|
||||
|
||||
string m_uri, m_scheme, m_path;
|
||||
queue<pair<string, string> > m_keyValuePairs;
|
||||
};
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
UNIT_TEST(UriValidScheme)
|
||||
{
|
||||
char const uriS[] = "mapswithme://map?ll=10.3,12.3223&n=Hello%20World";
|
||||
Uri uri(uriS, ARRAY_SIZE(uriS) - 1);
|
||||
TEST_EQUAL(uri.GetScheme(), "mapswithme", ());
|
||||
}
|
||||
|
||||
UNIT_TEST(UriInvalidSchemeNoColon)
|
||||
{
|
||||
TEST_EQUAL(Uri("mapswithme:").GetScheme(), "mapswithme", ());
|
||||
}
|
||||
|
||||
UNIT_TEST(UriTestValidScheme2)
|
||||
{
|
||||
TestUri("mapswithme://map?ll=10.3,12.3223&n=Hello%20World")
|
||||
.Scheme("mapswithme")
|
||||
.Path("map")
|
||||
.KV("ll", "10.3,12.3223")
|
||||
.KV("n", "Hello World");
|
||||
}
|
||||
|
||||
UNIT_TEST(UriComprehensive)
|
||||
{
|
||||
TestUri("");
|
||||
|
||||
TestUri("scheme:").Scheme("scheme");
|
||||
|
||||
TestUri("scheme:/").Scheme("scheme");
|
||||
|
||||
TestUri("scheme://").Scheme("scheme");
|
||||
|
||||
TestUri("sometext");
|
||||
|
||||
TestUri(":noscheme");
|
||||
|
||||
TestUri("://noscheme?");
|
||||
|
||||
TestUri("mwm://?").Scheme("mwm");
|
||||
|
||||
TestUri("http://path/to/something").Scheme("http").Path("path/to/something");
|
||||
|
||||
TestUri("http://path?").Scheme("http").Path("path");
|
||||
|
||||
TestUri("maps://path?&&key=&").Scheme("maps").Path("path").KV("key", "");
|
||||
|
||||
TestUri("mapswithme://map?ll=1.2,3.4&z=15").Scheme("mapswithme").Path("map")
|
||||
.KV("ll", "1.2,3.4").KV("z", "15");
|
||||
|
||||
TestUri("nopathnovalues://?key1&key2=val2").Scheme("nopathnovalues").Path("")
|
||||
.KV("key1", "").KV("key2", "val2");
|
||||
|
||||
TestUri("s://?key1&key2").Scheme("s").Path("").KV("key1", "").KV("key2", "");
|
||||
|
||||
TestUri("g://p?key1=val1&key2=").Scheme("g").Path("p").KV("key1", "val1").KV("key2", "");
|
||||
|
||||
TestUri("g://p?=val1&key2=").Scheme("g").Path("p").KV("", "val1").KV("key2", "");
|
||||
|
||||
TestUri("g://?k&key2").Scheme("g").KV("k", "").KV("key2", "");
|
||||
|
||||
TestUri("m:?%26Amp%26%3D%26Amp%26&name=%31%20%30").Scheme("m")
|
||||
.KV("&Amp&=&Amp&", "").KV("name", "1 0");
|
||||
|
||||
TestUri("s://?key1=value1&key1=value2&key1=value3&key2&key2&key3=value1&key3&key3=value2")
|
||||
.Scheme("s")
|
||||
.KV("key1", "value1").KV("key1", "value2").KV("key1", "value3")
|
||||
.KV("key2", "").KV("key2", "")
|
||||
.KV("key3", "value1").KV("key3", "").KV("key3", "value2");
|
||||
}
|
69
coding/uri.cpp
Normal file
69
coding/uri.cpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#include "uri.hpp"
|
||||
#include "url_encode.hpp"
|
||||
#include "../base/logging.hpp"
|
||||
#include "../std/algorithm.hpp"
|
||||
|
||||
using namespace url_scheme;
|
||||
|
||||
void Uri::Init()
|
||||
{
|
||||
if (!Parse())
|
||||
{
|
||||
m_scheme.clear();
|
||||
m_path.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool Uri::Parse()
|
||||
{
|
||||
// get url scheme
|
||||
size_t pathStart = m_url.find(':');
|
||||
if (pathStart == string::npos || pathStart == 0)
|
||||
return false;
|
||||
m_scheme.assign(m_url, 0, pathStart);
|
||||
|
||||
// skip slashes
|
||||
while (++pathStart < m_url.size() && m_url[pathStart] == '/') {};
|
||||
|
||||
// get path
|
||||
m_queryStart = m_url.find('?', pathStart);
|
||||
m_path.assign(m_url, pathStart, m_queryStart - pathStart);
|
||||
|
||||
// url without query
|
||||
if (m_queryStart == string::npos)
|
||||
m_queryStart = m_url.size();
|
||||
else
|
||||
++m_queryStart;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Uri::ForEachKeyValue(CallbackT const & callback) const
|
||||
{
|
||||
// parse query for keys and values
|
||||
for (size_t start = m_queryStart; start < m_url.size(); )
|
||||
{
|
||||
// TODO: Unoptimal search here, since it goes until the end of the string.
|
||||
size_t const end = min(m_url.size(), m_url.find('&', start));
|
||||
|
||||
// Skip empty keys.
|
||||
if (end - start > 0)
|
||||
{
|
||||
size_t const eq = m_url.find('=', start);
|
||||
|
||||
string key, value;
|
||||
if (eq < end)
|
||||
{
|
||||
key = UrlDecode(m_url.substr(start, eq - start));
|
||||
value = UrlDecode(m_url.substr(eq + 1, end - eq - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
key = UrlDecode(m_url.substr(start, end - start));
|
||||
}
|
||||
callback(key, value);
|
||||
}
|
||||
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
36
coding/uri.hpp
Normal file
36
coding/uri.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include "../base/base.hpp"
|
||||
#include "../std/function.hpp"
|
||||
#include "../std/string.hpp"
|
||||
|
||||
namespace url_scheme
|
||||
{
|
||||
|
||||
// Uri in format: 'scheme://path?key1=value1&key2&key3=&key4=value4'
|
||||
class Uri
|
||||
{
|
||||
public:
|
||||
typedef function<void (string const &, string const &)> CallbackT;
|
||||
|
||||
explicit Uri(string const & uri) : m_url(uri) { Init(); }
|
||||
Uri(char const * uri, size_t size) : m_url(uri, uri + size) { Init(); }
|
||||
|
||||
string GetScheme() const { return m_scheme; }
|
||||
string GetPath() const { return m_path; }
|
||||
bool IsValid() const { return !m_scheme.empty(); }
|
||||
|
||||
void ForEachKeyValue(CallbackT const & callback) const;
|
||||
|
||||
private:
|
||||
void Init();
|
||||
bool Parse();
|
||||
|
||||
string m_url;
|
||||
string m_scheme;
|
||||
string m_path;
|
||||
|
||||
size_t m_queryStart;
|
||||
};
|
||||
|
||||
} // namespace url_scheme
|
Loading…
Add table
Reference in a new issue