Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/coding
diff options
context:
space:
mode:
authorYury Melnichek <melnichek@gmail.com>2012-11-28 20:50:24 +0400
committerAlex Zolotarev <alex@maps.me>2015-09-23 01:47:30 +0300
commitf93a18c9bdcd419a38301db889dd7fecd5e4e99c (patch)
tree64d90d41947652ee6d94a2e42caf15b2466b06c5 /coding
parent9ae450222c678bfdc53136db7fbd61cd401768f4 (diff)
[coding] Add Uri class.
Diffstat (limited to 'coding')
-rw-r--r--coding/coding.pro2
-rw-r--r--coding/coding_tests/coding_tests.pro1
-rw-r--r--coding/coding_tests/uri_test.cpp117
-rw-r--r--coding/uri.cpp69
-rw-r--r--coding/uri.hpp36
5 files changed, 225 insertions, 0 deletions
diff --git a/coding/coding.pro b/coding/coding.pro
index fac05657ed..dd977258f6 100644
--- a/coding/coding.pro
+++ b/coding/coding.pro
@@ -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 \
diff --git a/coding/coding_tests/coding_tests.pro b/coding/coding_tests/coding_tests.pro
index 2db011c1aa..1eb3c5edc7 100644
--- a/coding/coding_tests/coding_tests.pro
+++ b/coding/coding_tests/coding_tests.pro
@@ -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 \
diff --git a/coding/coding_tests/uri_test.cpp b/coding/coding_tests/uri_test.cpp
new file mode 100644
index 0000000000..73b2073460
--- /dev/null
+++ b/coding/coding_tests/uri_test.cpp
@@ -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");
+}
diff --git a/coding/uri.cpp b/coding/uri.cpp
new file mode 100644
index 0000000000..9244219045
--- /dev/null
+++ b/coding/uri.cpp
@@ -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;
+ }
+}
diff --git a/coding/uri.hpp b/coding/uri.hpp
new file mode 100644
index 0000000000..305d34b566
--- /dev/null
+++ b/coding/uri.hpp
@@ -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