diff options
-rw-r--r-- | raul/Path.hpp | 63 | ||||
-rw-r--r-- | raul/URI.hpp | 111 | ||||
-rw-r--r-- | src/Path.cpp | 72 | ||||
-rw-r--r-- | tests/path_test.cpp | 5 |
4 files changed, 184 insertions, 67 deletions
diff --git a/raul/Path.hpp b/raul/Path.hpp index b6b0b60..bb666de 100644 --- a/raul/Path.hpp +++ b/raul/Path.hpp @@ -25,6 +25,7 @@ #include <cassert> #include "raul/Symbol.hpp" +#include "raul/URI.hpp" namespace Raul { @@ -43,11 +44,23 @@ namespace Raul { * * \ingroup raul */ -class Path : public std::basic_string<char> { +class Path : public URI { public: + class BadPath : public std::exception { + public: + BadPath(const std::string& path) : _path(path) {} + ~BadPath() throw() {} + const char* what() const throw() { return _path.c_str(); } + private: + std::string _path; + }; + + static const std::string prefix; + static const size_t prefix_len; + static const std::string root_uri; /** Construct an uninitialzed path, because the STL is annoying. */ - Path() : std::basic_string<char>("/") {} + Path() : URI(root_uri) {} /** Construct a Path from an std::string. * @@ -55,21 +68,22 @@ public: * use is_valid first to check. */ Path(const std::basic_string<char>& path) - : std::basic_string<char>(path) + : URI((path.find(":") == std::string::npos) ? prefix + path : path) { - assert(is_valid(path)); + if (!is_valid(str())) + throw BadPath(str()); } - /** Construct a Path from a C string. * * It is a fatal error to construct a Path from an invalid string, * use is_valid first to check. */ Path(const char* cpath) - : std::basic_string<char>(cpath) + : URI((std::string(cpath).find(":") == std::string::npos) ? prefix + cpath : cpath) { - assert(is_valid(cpath)); + if (!is_valid(str())) + throw BadPath(str()); } static bool is_valid(const std::basic_string<char>& path); @@ -82,18 +96,28 @@ public: static std::string pathify(const std::basic_string<char>& str); static std::string nameify(const std::basic_string<char>& str); - static void replace_invalid_chars(std::string& str, bool replace_slash = false); + static void replace_invalid_chars(std::string& str, size_t start, bool replace_slash = false); + bool is_root() const { return str() == root_uri; } + bool is_child_of(const Path& parent) const; bool is_parent_of(const Path& child) const; + + Path child(const std::string& s) const { + if (is_valid(s)) + return std::string(base()) + Path(s).chop_scheme().substr(1); + else + return std::string(base()) + s; + } + Path operator+(const Path& p) const { return child(p); } /** Return the name of this object (everything after the last '/'). * This is the "method name" for OSC paths. * The empty string may be returned (if the path is "/"). */ inline std::string name() const { - if ((*this) == "/") + if (str() == root_uri) return ""; else return substr(find_last_of("/")+1); @@ -115,15 +139,19 @@ public: * This is the (deepest) "container path" for OSC paths. */ inline Path parent() const { - std::basic_string<char> parent = substr(0, find_last_of("/")); - return (parent == "") ? "/" : parent; + if (str() == root_uri) { + return str(); + } else { + size_t last_slash = find_last_of("/"); + return (last_slash == prefix_len) ? root_uri : substr(0, last_slash); + } } - /** Return path relative to soe base path (chop prefix) + /** Return path relative to some base path (chop prefix) */ inline Path relative_to_base(const Path& base) const { - if ((*this) == base) { + if (str() == base) { return "/"; } else { assert(length() > base.length()); @@ -138,17 +166,18 @@ public: * child path can be made using parent.base() + child_name. */ inline const std::string base() const { - if ((*this) == "/") - return *this; + if (str() == root_uri) + return str(); else - return (*this) + "/"; + return str() + "/"; } + /** Return true if \a child is equal to, or a descendant of \a parent */ static bool descendant_comparator(const Path& parent, const Path& child) { return ( child == parent || (child.length() > parent.length() && (!std::strncmp(parent.c_str(), child.c_str(), parent.length()) - && (parent == "/" || child[parent.length()] == '/'))) ); + && (parent.str() == root_uri || child[parent.length()] == '/'))) ); } }; diff --git a/raul/URI.hpp b/raul/URI.hpp new file mode 100644 index 0000000..5967609 --- /dev/null +++ b/raul/URI.hpp @@ -0,0 +1,111 @@ +/* This file is part of Raul. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Raul is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Raul is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RAUL_URI_HPP +#define RAUL_URI_HPP + +#include <string> +#include <exception> +#include <ostream> + +namespace Raul { + + +/** Simple wrapper around standard string with useful URI-specific methods. + * + * This "should" be used for proper URIs (RFC3986), but not much support or + * validation is built-in yet. The URI string MUST have a scheme though. + */ +class URI : protected std::basic_string<char> { +public: + class BadURI : public std::exception { + public: + BadURI(const std::string& uri) : _uri(uri) {} + ~BadURI() throw() {} + const char* what() const throw() { return _uri.c_str(); } + private: + std::string _uri; + }; + + /** Construct a URI from an std::string. + * + * It is a fatal error to construct a URI from an invalid string, + * use is_valid first to check. + */ + URI(const std::basic_string<char>& uri) : std::basic_string<char>(uri) { + if (!is_valid(uri)) + throw BadURI(uri); + } + + /** Construct a URI from a C string. + * + * It is a fatal error to construct a URI from an invalid string, + * use is_valid first to check. + */ + URI(const char* uri) : std::basic_string<char>(uri) { + if (!is_valid(uri)) + throw BadURI(uri); + } + + static bool is_valid(const std::basic_string<char>& uri) { + return uri.find(":") != std::string::npos; + } + + /** Return path with every up to and including the first occurence of str */ + inline const std::string chop_start(const std::string& str) const { + return substr(find(str) + str.length()); + } + + /** Return the URI with the scheme removed (as a string) */ + std::string chop_scheme() const { return chop_start(":"); } + + /** Return the URI scheme (everything before the first ':') */ + inline std::string name() const { return substr(0, find(":")); } + + inline const std::string& str() const { return *this; } + inline const char* c_str() const { return str().c_str(); } + + inline operator const std::string() const { return str(); } + inline operator std::string() { return str(); } + + inline std::string substr(size_t start, size_t end=std::string::npos) const { + return str().substr(start, end); + } + + inline bool operator<(const URI& uri) const { return str() < uri; } + inline bool operator<=(const URI& uri) const { return str() <= uri; } + inline bool operator>(const URI& uri) const { return str() > uri; } + inline bool operator>=(const URI& uri) const { return str() >= uri; } + inline bool operator==(const URI& uri) const { return str() == uri; } + inline bool operator!=(const URI& uri) const { return str() != uri; } + + inline size_t length() const { return str().length(); } + inline size_t find(const std::string& s) const { return str().find(s); } + inline size_t find_last_of(const std::string& s) const { return str().find_last_of(s); } +}; + +static inline +std::ostream& +operator<<(std::ostream& os, const URI& uri) +{ + return (os << uri.str()); +} + +} // namespace Raul + +#endif // RAUL_URI_HPP + diff --git a/src/Path.cpp b/src/Path.cpp index bd4a17a..cec42fd 100644 --- a/src/Path.cpp +++ b/src/Path.cpp @@ -21,10 +21,16 @@ using namespace std; namespace Raul { +const std::string Path::prefix = "path:"; +const size_t Path::prefix_len = 5; +const std::string Path::root_uri = Path::prefix + "/"; bool -Path::is_valid(const std::basic_string<char>& path) +Path::is_valid(const std::basic_string<char>& path_str) { + const size_t colon = path_str.find(":"); + const string path = (colon == string::npos) ? path_str : path_str.substr(colon + 1); + if (path.length() == 0) return false; @@ -42,7 +48,6 @@ Path::is_valid(const std::basic_string<char>& path) if (path.find("//") != string::npos) return false; - for (size_t i=0; i < path.length(); ++i) // All contained symbols must not start with a digit if (i > 0 && path[i-1] == '/' && isdigit(path[i])) @@ -54,20 +59,6 @@ Path::is_valid(const std::basic_string<char>& path) && (path[i] < '0' || path[i] > '9') ) return false; -#if 0 - // Disallowed characters - if ( path.find(" ") != string::npos - || path.find("#") != string::npos - || path.find("*") != string::npos - || path.find(",") != string::npos - || path.find("?") != string::npos - || path.find("[") != string::npos - || path.find("]") != string::npos - || path.find("{") != string::npos - || path.find("}") != string::npos) - return false; -#endif - return true; } @@ -80,22 +71,23 @@ Path::is_valid(const std::basic_string<char>& path) string Path::pathify(const std::basic_string<char>& str) { - string path = str; - - if (path.length() == 0) - return "/"; // this might not be wise + if (str.length() == 0) + return root_uri; // this might not be wise? + + string path = (str.substr(0, prefix_len) == prefix) ? str : prefix + str; + size_t start = prefix_len + 1; // Must start with a / - if (path.at(0) != '/') + if (path.at(start) != '/') path = string("/").append(path); // Must not end with a slash unless "/" - if (path.length() > 1 && path.at(path.length()-1) == '/') + if (path.length() > prefix_len + 1 && path.at(path.length()-1) == '/') path = path.substr(0, path.length()-1); // chop trailing slash assert(path.find_last_of("/") != string::npos); - replace_invalid_chars(path, false); + replace_invalid_chars(path, start, false); assert(is_valid(path)); @@ -115,7 +107,7 @@ Path::nameify(const std::basic_string<char>& str) if (name.length() == 0) return "_"; // this might not be wise - replace_invalid_chars(name, true); + replace_invalid_chars(name, 0, true); assert(is_valid(string("/") + name)); @@ -126,8 +118,11 @@ Path::nameify(const std::basic_string<char>& str) /** Replace any invalid characters in @a str with a suitable replacement. */ void -Path::replace_invalid_chars(string& str, bool replace_slash) +Path::replace_invalid_chars(string& str, size_t start, bool replace_slash) { + string prefix = str.substr(0, start); + str = str.substr(start); + size_t open_bracket = str.find_first_of('('); if (open_bracket != string::npos) str = str.substr(0, open_bracket); @@ -139,25 +134,6 @@ Path::replace_invalid_chars(string& str, bool replace_slash) if (str[str.length()-1] == ' ') str = str.substr(0, str.length()-1); -//#define STRICT_SYMBOLS - -#ifdef STRICT_SYMBOLS - // dB = special case, need to avoid CamelCase killing below - while (true) { - size_t decibels = str.find("dB"); - if (decibels != string::npos) - str[decibels+1] = 'b'; - else - break; - } - - // Kill CamelCase in favour of god_fearing_symbol_names - for (size_t i=1; i < str.length(); ++i) { - if (str[i] >= 'A' && str[i] <= 'Z' && str[i-1] >= 'a' && str[i-1] <= 'z') - str = str.substr(0, i) + '_' + str.substr(i); - } -#endif - if (isdigit(str[0])) str = string("_").append(str); @@ -166,15 +142,9 @@ Path::replace_invalid_chars(string& str, bool replace_slash) str = str.substr(0, i) + "_" + str.substr(i); } else if (str[i] == '\'') { str = str.substr(0, i) + str.substr(i+1); -#ifdef STRICT_SYMBOLS - } else if (str[i] >= 'A' && str[i] <= 'Z') { - str[i] = std::tolower(str[i]); -#endif } else if ( str[i] != '_' && str[i] != '/' && (str[i] < 'a' || str[i] > 'z') -#ifndef STRICT_SYMBOLS && (str[i] < 'A' || str[i] > 'Z') -#endif && (str[i] < '0' || str[i] > '9') ) { if (i > 0 && str[i-1] == '_') { str = str.substr(0, i) + str.substr(i+1); @@ -189,6 +159,8 @@ Path::replace_invalid_chars(string& str, bool replace_slash) if (str.length() != 1 && str[str.length()-1] == '_') str = str.substr(0, str.length()-1); + + str = prefix + str; } diff --git a/tests/path_test.cpp b/tests/path_test.cpp index 3bd14c9..ac7ff2b 100644 --- a/tests/path_test.cpp +++ b/tests/path_test.cpp @@ -23,6 +23,11 @@ main() cerr << "Nameification:" << endl; for (list<string>::iterator i = names.begin(); i != names.end(); ++i) cerr << *i << " -> " << Path::nameify(*i) << endl; + + cerr << endl; + cerr << Path("/foo/bar") << " parent = " << Path("/foo/bar").parent() << endl; + cerr << Path("/foo") << " parent = " << Path("/foo").parent() << endl; + cerr << Path("/") << " parent = " << Path("/").parent() << endl; cerr << "1's are good..." << endl << endl; |