diff options
author | hjk <hjk@qt.io> | 2021-05-18 18:03:10 +0200 |
---|---|---|
committer | hjk <hjk@qt.io> | 2021-05-19 11:41:42 +0000 |
commit | 2db9ebc61504c5455f1fb2b4ed2a1cb6115ccbfa (patch) | |
tree | b3fabec7fcf9d1df7f31ef71468dd120da9dffdc | |
parent | b3ee704f175e853637c2784134693aadc9b31314 (diff) | |
download | qt-creator-2db9ebc61504c5455f1fb2b4ed2a1cb6115ccbfa.tar.gz |
Utils: Rework FilePath guts
This replaces FilePath::m_url by explicit host and scheme members
This removes the unclear duplication of information in m_data and
m_url.path() and avoids syntactic restrictions a real url has on
scheme and host and complex (and potentially lossy) url parsing:
QUrl url;
url.setHost("1234");
assert(url.host() == "0.0.4.210")
The toString/fromString methods now accept strings of the form
scheme://host/path, mimicing the use of urls so far.
Relative remote paths are representable, their toString() output
looks like scheme://./host/path and can be parsed back by fromString().
Change-Id: Id3b52c270b228e23834faa3f44daad8b1718b654
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
-rw-r--r-- | src/libs/utils/fileutils.cpp | 128 | ||||
-rw-r--r-- | src/libs/utils/fileutils.h | 13 | ||||
-rw-r--r-- | tests/auto/utils/fileutils/tst_fileutils.cpp | 72 |
3 files changed, 157 insertions, 56 deletions
diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 1090f7e23a..a37a3f9b0b 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -254,12 +254,13 @@ FilePath FilePath::operator/(const QString &str) const void FilePath::clear() { m_data.clear(); - m_url.clear(); + m_host.clear(); + m_scheme.clear(); } bool FilePath::isEmpty() const { - return m_data.isEmpty() && !m_url.isValid(); + return m_data.isEmpty(); } /*! @@ -676,7 +677,8 @@ QFileInfo FilePath::toFileInfo() const FilePath FilePath::fromUrl(const QUrl &url) { FilePath fn; - fn.m_url = url; + fn.m_scheme = url.scheme(); + fn.m_host = url.host(); fn.m_data = url.path(); return fn; } @@ -684,12 +686,20 @@ FilePath FilePath::fromUrl(const QUrl &url) /// \returns a QString for passing on to QString based APIs QString FilePath::toString() const { - return m_data; + if (m_scheme.isEmpty()) + return m_data; + if (m_data.startsWith('/')) + return m_scheme + "://" + m_host + m_data; + return m_scheme + "://" + m_host + "/./" + m_data; } QUrl FilePath::toUrl() const { - return m_url; + QUrl url; + url.setScheme(m_scheme); + url.setHost(m_host); + url.setPath(m_data); + return url; } void FilePath::setDeviceFileHooks(const DeviceFileHooks &hooks) @@ -701,9 +711,9 @@ void FilePath::setDeviceFileHooks(const DeviceFileHooks &hooks) /// Converts the separators to the native format QString FilePath::toUserOutput() const { - if (m_url.isEmpty()) - return QDir::toNativeSeparators(toString()); - return m_url.toString(); + if (m_scheme.isEmpty()) + return QDir::toNativeSeparators(m_data); + return toString(); } QString FilePath::fileName() const @@ -737,13 +747,19 @@ QString FilePath::fileNameWithPathComponents(int pathComponents) const return m_data; } -QString FilePath::path() const +void FilePath::setScheme(const QString &scheme) { - if (!m_data.isEmpty()) - return m_data; - return m_url.path(); + QTC_CHECK(!scheme.contains('/')); + m_scheme = scheme; } +void FilePath::setHost(const QString &host) +{ + QTC_CHECK(!host.contains('/')); + m_host = host; +} + + /// \returns a bool indicating whether a file with this /// FilePath exists. bool FilePath::exists() const @@ -844,7 +860,7 @@ QByteArray FilePath::fileContents(int maxSize) const bool FilePath::needsDevice() const { - return m_url.isValid(); + return !m_scheme.isEmpty(); } @@ -899,7 +915,26 @@ FilePath FilePath::absoluteFromRelativePath(const FilePath &anchor) const FilePath FilePath::fromString(const QString &filename) { FilePath fn; - fn.m_data = filename; + if (filename.startsWith('/')) { + fn.m_data = filename; // fast track: absolute local paths + } else { + int pos1 = filename.indexOf("://"); + if (pos1 >= 0) { + fn.m_scheme = filename.left(pos1); + pos1 += 3; + int pos2 = filename.indexOf('/', pos1); + if (pos2 == -1) { + fn.m_data = filename.mid(pos1); + } else { + fn.m_host = filename.mid(pos1, pos2 - pos1); + fn.m_data = filename.mid(pos2); + } + if (fn.m_data.startsWith("/./")) + fn.m_data = fn.m_data.mid(3); + } else { + fn.m_data = filename; // treat everything else as local, too. + } + } return fn; } @@ -949,9 +984,7 @@ FilePath FilePath::fromVariant(const QVariant &variant) QVariant FilePath::toVariant() const { - if (!m_url.isEmpty()) - return m_url; - return m_data; + return toString(); } QDir FilePath::toDir() const @@ -961,9 +994,12 @@ QDir FilePath::toDir() const bool FilePath::operator==(const FilePath &other) const { - if (!m_url.isEmpty()) - return m_url == other.m_url; - return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) == 0; + if (m_scheme.isEmpty()) + return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) == 0; + + // FIXME: This should take the host's file name case sensitivity into account. + // The first approximation here is "Anything unusual is not case sensitive" + return m_data == other.m_data && m_host == other.m_host && m_scheme == other.m_scheme; } bool FilePath::operator!=(const FilePath &other) const @@ -973,9 +1009,16 @@ bool FilePath::operator!=(const FilePath &other) const bool FilePath::operator<(const FilePath &other) const { - if (!m_url.isEmpty()) - return m_url < other.m_url; - return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) < 0; + if (m_scheme.isEmpty()) + return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) < 0; + + // FIXME: This should take the host's file name case sensitivity into account. + // The first approximation here is "Anything unusual is not case sensitive" + if (m_data != other.m_data) + return m_data < other.m_data; + if (m_host != other.m_host) + return m_host < other.m_host; + return m_scheme < other.m_scheme; } bool FilePath::operator<=(const FilePath &other) const @@ -1034,7 +1077,7 @@ bool FilePath::endsWith(const QString &s) const bool FilePath::isDir() const { - QTC_CHECK(m_url.isEmpty()); // FIXME: Not implemented yet. + QTC_CHECK(m_scheme.isEmpty()); // FIXME: Not implemented yet. return QFileInfo(m_data).isDir(); } @@ -1150,26 +1193,10 @@ QString FilePath::calcRelativePath(const QString &absolutePath, const QString &a */ FilePath FilePath::onDevice(const FilePath &deviceTemplate) const { - FilePath res = *this; - - if (res.m_url.isValid()) { - if (deviceTemplate.m_url.isValid()) { - const QString path = m_url.path(); - res.m_url = deviceTemplate.toUrl(); - res.m_url.setPath(path); - } else { - res.m_data = deviceTemplate.m_data; - res.m_url.clear(); - } - } else { - if (deviceTemplate.m_url.isValid()) { - res.m_url = deviceTemplate.m_url; - res.m_url.setPath(m_data); - res.m_data.clear(); - } else { - // Nothing to do. - } - } + FilePath res; + res.m_data = m_data; + res.m_host = deviceTemplate.m_host; + res.m_scheme = deviceTemplate.m_scheme; return res; } @@ -1178,16 +1205,9 @@ FilePath FilePath::pathAppended(const QString &str) const FilePath fn = *this; if (str.isEmpty()) return fn; - if (fn.m_url.isValid()) { - QString path = fn.m_url.path(); - if (!path.isEmpty() && !path.endsWith(QLatin1Char('/'))) - path.append('/'); - fn.m_url.setPath(path); - } else { - if (!fn.m_data.isEmpty() && !fn.m_data.endsWith(QLatin1Char('/'))) - fn.m_data.append('/'); - fn.m_data.append(str); - } + if (!fn.m_data.isEmpty() && !fn.m_data.endsWith(QLatin1Char('/'))) + fn.m_data.append('/'); + fn.m_data.append(str); return fn; } diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index c8108a8a74..8da4f64fdc 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -102,7 +102,15 @@ public: QString fileName() const; QString fileNameWithPathComponents(int pathComponents) const; - QString path() const; + + QString scheme() const { return m_scheme; } + void setScheme(const QString &scheme); + + QString host() const { return m_host; } + void setHost(const QString &host); + + QString path() const { return m_data; } + void setPath(const QString &path) { m_data = path; } bool needsDevice() const; bool exists() const; @@ -165,8 +173,9 @@ private: friend class ::tst_fileutils; static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath); + QString m_scheme; + QString m_host; QString m_data; - QUrl m_url; }; QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FilePath &fn); diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp index cd94526a7a..258422305f 100644 --- a/tests/auto/utils/fileutils/tst_fileutils.cpp +++ b/tests/auto/utils/fileutils/tst_fileutils.cpp @@ -50,6 +50,8 @@ private slots: void relativePath_specials(); void relativePath_data(); void relativePath(); + void fromToString_data(); + void fromToString(); private: QTemporaryDir tempDir; @@ -257,5 +259,75 @@ void tst_fileutils::relativePath() QCOMPARE(actualPath.toString(), result); } +void tst_fileutils::fromToString_data() +{ + QTest::addColumn<QString>("scheme"); + QTest::addColumn<QString>("host"); + QTest::addColumn<QString>("path"); + QTest::addColumn<QString>("full"); + + QTest::newRow("s0") << "" << "" << "" << ""; + QTest::newRow("s1") << "" << "" << "/" << "/"; + QTest::newRow("s2") << "" << "" << "a/b/c/d" << "a/b/c/d"; + QTest::newRow("s3") << "" << "" << "/a/b" << "/a/b"; + + QTest::newRow("s4") + << "docker" << "1234abcdef" << "/bin/ls" << "docker://1234abcdef/bin/ls"; + + QTest::newRow("s5") + << "docker" << "1234" << "/bin/ls" << "docker://1234/bin/ls"; + + // This is not a proper URL. + QTest::newRow("s6") + << "docker" << "1234" << "somefile" << "docker://1234/./somefile"; + + // Local Windows paths: + QTest::newRow("w1") << "" << "" << "C:/data" << "C:/data"; + QTest::newRow("w2") << "" << "" << "C:/" << "C:/"; + QTest::newRow("w3") << "" << "" << "//./com1" << "//./com1"; + QTest::newRow("w4") << "" << "" << "//?/path" << "//?/path"; + QTest::newRow("w5") << "" << "" << "/Global?\?/UNC/host" << "/Global?\?/UNC/host"; + QTest::newRow("w6") << "" << "" << "//server/dir/file" << "//server/dir/file"; + QTest::newRow("w7") << "" << "" << "//server/dir" << "//server/dir"; + QTest::newRow("w8") << "" << "" << "//server" << "//server"; + + // Not supported yet: "Remote" windows. Would require use of e.g. + // FileUtils::isRelativePath with support from the remote device + // identifying itself as Windows. + + // Actual (filePath.path()): "/C:/data" + // Expected (path) : "C:/data" + + //QTest::newRow("w9") << "scheme" << "server" << "C:/data" + // << "scheme://server/C:/data"; +} + +void tst_fileutils::fromToString() +{ + QFETCH(QString, full); + QFETCH(QString, scheme); + QFETCH(QString, host); + QFETCH(QString, path); + + FilePath filePath = FilePath::fromString(full); + + QCOMPARE(filePath.toString(), full); + + QCOMPARE(filePath.scheme(), scheme); + QCOMPARE(filePath.host(), host); + QCOMPARE(filePath.path(), path); + + + FilePath copy = filePath; + copy.setHost(host); + QCOMPARE(copy.toString(), full); + + copy.setScheme(scheme); + QCOMPARE(copy.toString(), full); + + copy.setPath(path); + QCOMPARE(copy.toString(), full); +} + QTEST_APPLESS_MAIN(tst_fileutils) #include "tst_fileutils.moc" |