File indexing completed on 2024-05-05 04:38:46
0001 /* 0002 SPDX-FileCopyrightText: 2012 Milian Wolff <mail@milianw.de> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #ifndef KDEVELOP_PATH_H 0008 #define KDEVELOP_PATH_H 0009 0010 #include "utilexport.h" 0011 0012 #include <QMetaType> 0013 #include <QString> 0014 #include <QVector> 0015 #include <QUrl> 0016 0017 #include <algorithm> 0018 0019 namespace KDevelop { 0020 0021 /** 0022 * @return Return a string representation of @p url, if possible as local file 0023 * 0024 * Convenience method for working around https://bugreports.qt.io/browse/QTBUG-41729 0025 */ 0026 QString KDEVPLATFORMUTIL_EXPORT toUrlOrLocalFile(const QUrl& url, 0027 QUrl::FormattingOptions options = QUrl::FormattingOptions( QUrl::PrettyDecoded )); 0028 0029 /** 0030 * @brief Path data type optimized for memory consumption. 0031 * 0032 * This class holds data that represents a local or remote path. 0033 * In the project model e.g. we usually store whole trees such as 0034 * 0035 * /foo/ 0036 * /foo/bar/ 0037 * /foo/bar/asdf.txt 0038 * 0039 * Normal QString/QUrl/QUrl types would not share any memory for these paths 0040 * at all. This class though can share the segments of the paths and thus 0041 * consume far less total memory. 0042 * 0043 * Just like the URL types, the Path can point to a remote location. 0044 * 0045 * Example for how to leverage memory sharing for the above input data: 0046 * 0047 * @code 0048 * Path foo("/foo"); 0049 * Path bar(foo, "bar"); 0050 * Path asdf(foo, "asdf.txt"); 0051 * @endcode 0052 * 0053 * @note Just as with QString e.g. you won't share any data implicitly when 0054 * you do something like this: 0055 * 0056 * @code 0057 * Path foo1("/foo"); 0058 * Path foo2("/foo"); 0059 * @endcode 0060 */ 0061 class KDEVPLATFORMUTIL_EXPORT Path 0062 { 0063 public: 0064 using List = QVector<Path>; 0065 0066 /** 0067 * Construct an empty, invalid Path. 0068 */ 0069 Path() = default; 0070 0071 /** 0072 * Create a Path out of a string representation of a path or URL. 0073 * 0074 * @note Not every kind of remote URL is supported, rather only path-like 0075 * URLs without fragments, queries, sub-Paths and the like are supported. 0076 * 0077 * Empty paths or URLs containing one of the following are considered invalid: 0078 * - URL fragments (i.e. "...#fragment") 0079 * - URL queries (i.e. "...?query=") 0080 * - sub-URLs (i.e. "file:///tmp/kde.tgz#gzip:/#tar:/kdevelop") 0081 * 0082 * @sa isValid() 0083 */ 0084 explicit Path(const QString& pathOrUrl); 0085 0086 /** 0087 * Convert a QUrl to a Path. 0088 * 0089 * @note Not every kind of remote URL is supported, rather only path-like 0090 * URLs without fragments, queries, sub-Paths and the like are supported. 0091 * 0092 * Empty paths or URLs containing one of the following are considered invalid: 0093 * - URL fragments (i.e. "...#fragment") 0094 * - URL queries (i.e. "...?query=") 0095 * - sub-URLs (i.e. "file:///tmp/kde.tgz#gzip:/#tar:/kdevelop") 0096 * 0097 * @sa isValid() 0098 */ 0099 explicit Path(const QUrl& url); 0100 0101 /** 0102 * Create a copy of @p base and append a path segment @p subPath. 0103 * 0104 * This implicitly shares the data of @p base and thus is very efficient 0105 * memory wise compared to creating two Paths from separate strings. 0106 * 0107 * @p subPath A relative or absolute path. If this is an absolute path then 0108 * the path in @p base will be ignored and only the remote data copied. If 0109 * this is a relative path it will be combined with @p base. 0110 * 0111 * @sa addPath() 0112 */ 0113 explicit Path(const Path& base, const QString& subPath); 0114 0115 friend void swap(Path& a, Path& b) noexcept 0116 { 0117 a.m_data.swap(b.m_data); 0118 } 0119 0120 /** 0121 * Equality comparison between @p other and this Path. 0122 * 0123 * @return true if @p other is equal to this Path. 0124 */ 0125 inline bool operator==(const Path& other) const 0126 { 0127 if (other.m_data.data() == m_data.data()) 0128 return true; // fast path when both containers point to the same shared data 0129 // The size check here is a bit faster than calling std::equal with 4 arguments. 0130 if (other.m_data.size() != m_data.size()) 0131 return false; 0132 // Optimization: compare in reverse order as often the mismatch is at the end, 0133 // while the first few path segments are usually the same in different paths. 0134 return std::equal(m_data.rbegin(), m_data.rend(), other.m_data.rbegin()); 0135 } 0136 0137 /** 0138 * Inequality comparison between @p other and this Path. 0139 * 0140 * @return true if @p other is different from this Path. 0141 */ 0142 inline bool operator!=(const Path& other) const 0143 { 0144 return !operator==(other); 0145 } 0146 0147 /** 0148 * Compares *this with @p other and returns an integer less than, equal to, 0149 * or greater than zero if *this is less than, equal to, or greater than @p other. 0150 * 0151 * If @p cs is Qt::CaseSensitive, the comparison is case sensitive; 0152 * otherwise the comparison is case insensitive. 0153 */ 0154 int compare(const Path& other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const; 0155 0156 /** 0157 * Less-than path comparison between @p other and this Path. 0158 * 0159 * @return true if this Path is less than @p other. 0160 */ 0161 bool operator<(const Path& other) const 0162 { 0163 return compare(other, Qt::CaseSensitive) < 0; 0164 } 0165 0166 /** 0167 * Greater-than path comparison between @p other and this Path. 0168 * 0169 * @return true if this Path is greater than @p other. 0170 */ 0171 inline bool operator>(const Path& other) const 0172 { 0173 return other < *this; 0174 } 0175 0176 /** 0177 * Less-than-equal path comparison between @p other and this Path. 0178 * 0179 * @return true if this Path is less than @p other or equal. 0180 */ 0181 inline bool operator<=(const Path& other) const 0182 { 0183 return !(other < *this); 0184 } 0185 0186 /** 0187 * Greater-than-equal path comparison between @p other and this Path. 0188 * 0189 * @return true if this Path is greater than @p other or equal. 0190 */ 0191 inline bool operator>=(const Path& other) const 0192 { 0193 return !(*this < other); 0194 } 0195 0196 /** 0197 * Check whether this Path is valid. 0198 * 0199 * @return true if the Path is valid, i.e. contains data, false otherwise. 0200 */ 0201 inline bool isValid() const 0202 { 0203 return !m_data.isEmpty(); 0204 } 0205 0206 /** 0207 * Check whether this Path is empty. 0208 * 0209 * @return true if the Path is empty, false otherwise, i.e. if it contains data. 0210 */ 0211 inline bool isEmpty() const 0212 { 0213 return m_data.isEmpty(); 0214 } 0215 0216 /** 0217 * Convert the Path to a string, yielding either the plain path for local 0218 * paths or the stringified URL for remote Paths. 0219 * 0220 * @return a string representation of this Path. 0221 * @sa path() 0222 */ 0223 QString pathOrUrl() const; 0224 0225 /** 0226 * Return the path segment of this Path. This is the same for local paths 0227 * as calling pathOrUrl(). The difference is only for remote paths which 0228 * would return the protocol, host etc. data in pathOrUrl but not here. 0229 * 0230 * @return the path segment of this Path. 0231 * 0232 * @sa pathOrUrl() 0233 */ 0234 QString path() const; 0235 0236 /** 0237 * Return the path for local path and an empty string for remote paths. 0238 */ 0239 QString toLocalFile() const; 0240 0241 /** 0242 * @return the relative path from this path to @p path. 0243 * 0244 * Examples: 0245 * @code 0246 * Path p1("/foo/bar"); 0247 * Path p2("/foo/bar/asdf/test.txt"); 0248 * p1.relativePath(p2); // returns: asdf/test.txt 0249 * Path p3("/foo/asdf/lala"); 0250 * p3.relativePath(p1); // returns: ../../bar 0251 * @endcode 0252 * 0253 * @sa QUrl::relativePath 0254 */ 0255 QString relativePath(const Path& path) const; 0256 0257 /** 0258 * @return True if this path is the parent of @p path. 0259 * 0260 * For instance, ftp://host/dir/ is a parent of ftp://host/dir/subdir/blub, 0261 * or /foo is a parent of /foo/bar. 0262 * 0263 * NOTE: Contrary to QUrl::isParentOf this returns false if the path equals this one. 0264 */ 0265 bool isParentOf(const Path& path) const; 0266 0267 /** 0268 * @return True if this path is the direct parent of @p path. 0269 * 0270 * For instance, ftp://host/dir/ is the direct parent of ftp://host/dir/subdir, 0271 * but ftp.//host/ is a parent but not the direct parent. 0272 * 0273 * This is more efficient than @code path.parent() == *this @endcode since 0274 * it does not require any temporary allocations as for the parent() call. 0275 */ 0276 bool isDirectParentOf(const Path& path) const; 0277 0278 /** 0279 * @return the prefix of a remote URL containing protocol, host, port etc. pp. 0280 * If this path is not remote, this returns an empty string. 0281 */ 0282 QString remotePrefix() const; 0283 0284 /** 0285 * @return a const reference to the internal data. 0286 * 0287 * @note Returning a reference to rather than a copy of QVector can substantially 0288 * improve performance of a tight loop that calls this function. 0289 * 0290 * TODO: return std::span once we can rely on C++20. 0291 */ 0292 inline const QVector<QString>& segments() const 0293 { 0294 return m_data; 0295 } 0296 0297 /** 0298 * @return the Path converted to a QUrl. 0299 */ 0300 QUrl toUrl() const; 0301 0302 /** 0303 * @return true when this Path points to a local file, false otherwise. 0304 */ 0305 bool isLocalFile() const; 0306 0307 /** 0308 * @return true when this Path points to a remote file, false otherwise. 0309 */ 0310 bool isRemote() const; 0311 0312 /** 0313 * @return the last element of the path. 0314 * 0315 * This will never return the remote URL prefix. 0316 */ 0317 QString lastPathSegment() const; 0318 0319 /** 0320 * Set the file name of this Path, i.e. the last element of the path. 0321 * 0322 * This will never overwrite the remote URL prefix. 0323 */ 0324 void setLastPathSegment(const QString& name); 0325 0326 /** 0327 * Append @p path to this Path. 0328 * 0329 * NOTE: If @p path starts with a slash, this function ignores it. 0330 * I.e. you cannot set the path this way. @sa QUrl::addPath 0331 */ 0332 void addPath(const QString& path); 0333 0334 /** 0335 * @return the path pointing to the parent folder of this path. 0336 * 0337 * @sa KIO::upUrl() 0338 */ 0339 Path parent() const; 0340 0341 /** 0342 * @return true when this path has a parent and false if this is a root or invalid path. 0343 */ 0344 bool hasParent() const; 0345 0346 /** 0347 * Change directory by relative path @p dir. 0348 * 0349 * NOTE: This is expensive. 0350 * 0351 * @sa QUrl::cd 0352 */ 0353 Path cd(const QString& dir) const; 0354 0355 private: 0356 // for remote urls the first element contains the a Path prefix 0357 // containing the protocol, user, port etc. pp. 0358 QVector<QString> m_data; 0359 }; 0360 0361 KDEVPLATFORMUTIL_EXPORT uint qHash(const Path& path); 0362 0363 /** 0364 * Convert the @p list of QUrls to a list of Paths. 0365 */ 0366 KDEVPLATFORMUTIL_EXPORT Path::List toPathList(const QList<QUrl>& list); 0367 0368 /** 0369 * Convert the @p list of QStrings to a list of Paths. 0370 */ 0371 KDEVPLATFORMUTIL_EXPORT Path::List toPathList(const QList<QString>& list); 0372 } 0373 0374 /** 0375 * qDebug() stream operator. Writes the string to the debug output. 0376 */ 0377 KDEVPLATFORMUTIL_EXPORT QDebug operator<<(QDebug s, const KDevelop::Path& string); 0378 0379 namespace QTest { 0380 0381 template<typename T> char* toString(const T&); 0382 /** 0383 * QTestLib integration to have nice output in e.g. QCOMPARE failures. 0384 */ 0385 template<> 0386 KDEVPLATFORMUTIL_EXPORT char* toString(const KDevelop::Path& path); 0387 } 0388 0389 Q_DECLARE_TYPEINFO(KDevelop::Path, Q_MOVABLE_TYPE); 0390 Q_DECLARE_METATYPE(KDevelop::Path) 0391 Q_DECLARE_TYPEINFO(KDevelop::Path::List, Q_MOVABLE_TYPE); 0392 0393 #endif // KDEVELOP_PATH_H