diff options
-rw-r--r-- | raul/Path.hpp | 130 | ||||
-rw-r--r-- | raul/Symbol.hpp | 3 | ||||
-rw-r--r-- | raul/URI.hpp | 25 | ||||
-rw-r--r-- | src/Path.cpp | 85 |
4 files changed, 99 insertions, 144 deletions
diff --git a/raul/Path.hpp b/raul/Path.hpp index 7404877..57bed80 100644 --- a/raul/Path.hpp +++ b/raul/Path.hpp @@ -20,27 +20,25 @@ #include <cassert> #include <cctype> #include <cstring> +#include <exception> #include <string> #include "raul/Symbol.hpp" -#include "raul/URI.hpp" namespace Raul { -/** A URI which is a path (for example a filesystem or OSC path). +/** A restricted path of Symbols separated by, and beginning with, "/". * * This enforces that a Path is a valid path, where each fragment is a valid * Symbol, separated by exactly one slash (/). * * A path is divided by slashes (/). The first character MUST be a slash, and * the last character MUST NOT be a slash (except in the special case of the - * root path "/", which is the only valid single-character path). A Path - * is actually a URI, the relative path is appended to the root URI - * automatically, so a Patch can always be used as a URI. + * root path "/", which is the only valid single-character path). * * \ingroup raul */ -class Path : public URI { +class Path { public: class BadPath : public std::exception { public: @@ -51,79 +49,59 @@ public: const std::string _path; }; - /** Return the root path. - * The default root path is the URI "path:/" - * - * A Path is either the root path, or a child of a root path (i.e. the root - * path followed by a sequence of Symbols separated by '/') - */ - static const Path root(); - - /** Set the root path. - * The default root path is the URI "path:/" - * - * Note this should be done on application start up. Changing the root - * path while any Path objects exist will break things horribly; don't! - * - * The root can be set to any URI, there are no restrictions on valid - * characters and such like there are for relative paths (but it must be - * a valid URI, i.e. begin with a scheme, and in particular not begin - * with '/'). Relative paths are appended to the root path's URI, - * i.e. every Path, as a string, begins with the root URI. The part after - * that is a strict path (a sequence of Symbols separated by '/'). - */ - static void set_root(const Raul::URI& uri); - - static bool is_path(const Raul::URI& uri); - /** Construct an uninitialzed path, because the STL is annoying. */ - Path() : URI(root()) {} + Path() : _str(g_intern_string("/")) {} /** Construct a Path from an std::string. * * It is a fatal error to construct a Path from an invalid string, * use is_valid first to check. */ - Path(const std::basic_string<char>& path); + Path(const std::basic_string<char>& path) throw (BadPath) + : _str(g_intern_string(path.c_str())) + { + if (!is_valid(path)) { + throw BadPath(path); + } + } /** 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); + Path(const char* cpath) throw(BadPath) + : _str(g_intern_string(cpath)) + { + if (!is_valid(cpath)) { + throw BadPath(cpath); + } + } /** Construct a Path from another path. * * This is faster than constructing a path from the other path's string * representation, since validity checking is avoided. */ - Path(const Path& copy) : URI(copy) {} + Path& operator=(const Path& other) { + _str = other._str; + return *this; + } static bool is_valid(const std::basic_string<char>& path); - static std::string pathify(const std::basic_string<char>& str); + /** Convert a string to a valid Path. */ + static Path pathify(const std::basic_string<char>& str); static void replace_invalid_chars(std::string& str, size_t start, bool replace_slash = false); - bool is_root() const { return (*this) == root(); } + bool is_root() const { return !strcmp(_str, "/"); } 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 base() + Path(s).chop_scheme().substr(1); - else - return base() + s; - } - - Path child(const Path& p) const { - return base() + p.chop_scheme().substr(1); - } - Path operator+(const Path& p) const { return child(p); } /** Return the lowest common ancestor of a and b. */ @@ -135,7 +113,7 @@ public: * The empty string may be returned (if the path is the root path). */ inline const char* symbol() const { - if ((*this) != root()) { + if (!is_root()) { const char* last_slash = strrchr(c_str(), '/'); if (last_slash) { return last_slash + 1; @@ -150,16 +128,20 @@ public: * This is the (deepest) "container path" for OSC paths. */ inline Path parent() const { - if ((*this) == root()) { + if (is_root()) { return *this; } else { const std::string str(this->str()); const size_t first_slash = str.find('/'); const size_t last_slash = str.find_last_of('/'); - return (first_slash == last_slash) ? root() : str.substr(0, last_slash); + return (first_slash == last_slash) ? Path("/") : str.substr(0, last_slash); } } + Path child(const Path& p) const { + return base() + p.str().substr(1); + } + /** Return the path's child with the given symbol */ inline Path child(const Raul::Symbol& symbol) const { @@ -173,7 +155,7 @@ public: return "/"; } else { assert(length() > base.base().length()); - return substr(base.base().length() - 1); + return str().substr(base.base().length() - 1); } } @@ -184,32 +166,48 @@ public: */ inline const std::string base() const { std::string ret = str(); - if ((*this) == root() && ret[ret.length() - 1] == '/') + if (is_root() && ret[ret.length() - 1] == '/') return ret; else return ret + '/'; } - /** Return path with a trailing "/". - * - * Returned value is guaranteed to be a valid parent path, i.e. a valid - * child path can be made using parent.base() + symbol. - */ - inline const std::string base_no_scheme() const { - return base().substr(find(":") + 1); - } - /** 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 == root() || child.str()[parent.length()] == '/'))) ); + return (child == parent || + (child.length() > parent.length() && + (!std::strncmp(parent.c_str(), child.c_str(), parent.length()) + && (parent.is_root() || child.str()[parent.length()] == '/'))) ); } + inline std::string substr(size_t start, size_t end=std::string::npos) const { + return str().substr(start, end); + } + + inline bool operator<(const Path& path) const { return strcmp(_str, path.c_str()) < 0; } + inline bool operator<=(const Path& path) const { return (*this) == path || (*this) < path; } + inline bool operator==(const Path& path) const { return _str == path._str; } + inline bool operator!=(const Path& path) const { return _str != path._str; } + + inline char operator[](int i) const { return _str[i]; } + + inline size_t length() const { return str().length(); } + inline size_t find(const std::string& s) const { return str().find(s); } + + inline const std::string str() const { return _str; } + inline const char* c_str() const { return _str; } + private: - inline Path(bool unchecked, const URI& uri) : URI(uri) {} + const char* _str; }; } // namespace Raul +static inline +std::ostream& +operator<<(std::ostream& os, const Raul::Path& path) +{ + return (os << path.c_str()); +} + #endif // RAUL_PATH_HPP diff --git a/raul/Symbol.hpp b/raul/Symbol.hpp index b872e3a..dcf1f04 100644 --- a/raul/Symbol.hpp +++ b/raul/Symbol.hpp @@ -104,7 +104,8 @@ private: } // namespace Raul -static inline std::ostream& operator<<(std::ostream& os, const Raul::Symbol& symbol) +static inline std::ostream& +operator<<(std::ostream& os, const Raul::Symbol& symbol) { return (os << symbol.c_str()); } diff --git a/raul/URI.hpp b/raul/URI.hpp index bc64b8c..83454ca 100644 --- a/raul/URI.hpp +++ b/raul/URI.hpp @@ -23,6 +23,8 @@ #include <ostream> #include <glib.h> +#include "raul/Path.hpp" + namespace Raul { /** Simple wrapper around standard string with useful URI-specific methods. @@ -47,11 +49,12 @@ public: * 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="nil:0") + URI(const std::basic_string<char>& uri="nil:0") throw(BadURI) : _str(g_intern_string(uri.c_str())) { - if (!is_valid(uri)) + if (!is_valid(uri)) { throw BadURI(uri); + } } /** Construct a URI from a C string. @@ -59,13 +62,18 @@ public: * It is a fatal error to construct a URI from an invalid string, * use is_valid first to check. */ - URI(const char* uri) + URI(const char* uri) throw(BadURI) : _str(g_intern_string(uri)) { if (!is_valid(uri)) throw BadURI(uri); } + /** Construct a URI from a base URI and a Path. */ + URI(const URI& base, const Path& path) + : _str(g_intern_string((base.str() + path.c_str()).c_str())) + {} + static bool is_valid(const std::basic_string<char>& uri) { return uri.find(":") != std::string::npos; } @@ -95,22 +103,23 @@ public: inline char operator[](int i) const { return _str[i]; } - 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(char c) const { return str().find_last_of(c); } + 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(char c) const { return str().find_last_of(c); } private: const char* _str; }; +} // namespace Raul + static inline std::ostream& -operator<<(std::ostream& os, const URI& uri) +operator<<(std::ostream& os, const Raul::URI& uri) { return (os << uri.c_str()); } -} // namespace Raul #endif // RAUL_URI_HPP diff --git a/src/Path.cpp b/src/Path.cpp index 2bf194d..e3a529d 100644 --- a/src/Path.cpp +++ b/src/Path.cpp @@ -22,61 +22,22 @@ using std::string; namespace Raul { -static URI root_uri("path:/"); - -const Path Path::root() { return Path(true, root_uri); } -void Path::set_root(const Raul::URI& uri) { root_uri = uri.str(); } - -bool -Path::is_path(const Raul::URI& uri) -{ - return uri.length() >= root_uri.length() - && uri.substr(0, root_uri.length()) == root_uri.str() - && Path::is_valid(uri.str()); -} - -Path::Path(const std::basic_string<char>& path) - : URI(path[0] == '/' ? root_uri.str() + path.substr(1) : path) -{ - if (!is_valid(str())) - throw BadPath(str()); -} - -Path::Path(const char* cpath) - : URI(cpath[0] == '/' ? root_uri.str() + (cpath + 1) : cpath) -{ - if (!is_valid(str())) - throw BadPath(str()); -} - bool -Path::is_valid(const std::basic_string<char>& path_str) +Path::is_valid(const std::basic_string<char>& path) { - if (path_str.length() == 0) + if (path.empty() || path[0] != '/') { return false; + } - if (path_str == root_uri.str()) + // Root path + if (path == "/") { return true; + } - if (path_str[0] != '/' && - (path_str.length() < root_uri.length() - || path_str.substr(0, root_uri.length()) != root_uri.str())) - return false; - - const string path = (path_str[0] == '/') - ? path_str - : path_str.substr(root_uri.length() - 1); - - // Must start with a / - if (path[0] != '/') - return false; - - // Must not end with a slash unless "/" - if (path.length() > 1 && path[path.length()-1] == '/') + // Not root, must not end with a slash + if (*path.rbegin() == '/') return false; - assert(path.find_last_of("/") != string::npos); - // Double slash not allowed if (path.find("//") != string::npos) return false; @@ -95,17 +56,11 @@ Path::is_valid(const std::basic_string<char>& path_str) return true; } -/** Convert a string to a valid full path. - * - * The returned string is a valid relative path without the root prefix, - * i.e. the returned string starts with '/' followed by valid symbols, - * each separated by '/'. - */ -string +Path Path::pathify(const std::basic_string<char>& str) { if (str.length() == 0) - return root().chop_scheme(); // this might not be wise? + return Path("/"); // this might not be wise? const size_t first_slash = str.find('/'); string path = (first_slash == string::npos) @@ -120,13 +75,8 @@ Path::pathify(const std::basic_string<char>& str) if (path != "/" && path[path.length() - 1] == '/') path = path.substr(0, path.length() - 1); // chop trailing slash - assert(path.find_last_of('/') != string::npos); - replace_invalid_chars(path, 0, false); - - assert(is_valid(path)); - - return path; + return Path(path); } /** Replace any invalid characters in @a str with a suitable replacement. @@ -181,7 +131,7 @@ bool Path::is_child_of(const Path& parent) const { const string parent_base = parent.base(); - return (substr(0, parent_base.length()) == parent_base); + return (str().substr(0, parent_base.length()) == parent_base); } bool @@ -191,11 +141,9 @@ Path::is_parent_of(const Path& child) const } Path -Path::lca(const Path& patha, const Path& pathb) +Path::lca(const Path& a, const Path& b) { - const std::string a = patha.chop_scheme(); - const std::string b = pathb.chop_scheme(); - size_t len = std::min(a.length(), b.length()); + const size_t len = std::min(a.length(), b.length()); size_t last_slash = 0; for (size_t i = 0; i < len; ++i) { @@ -208,10 +156,9 @@ Path::lca(const Path& patha, const Path& pathb) } if (last_slash <= 1) { - return root(); + return Path("/"); } - return Path(a.substr(0, last_slash)); + return Path(a.str().substr(0, last_slash)); } } // namespace Raul - |