summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhjk <hjk@qt.io>2021-05-18 18:03:10 +0200
committerhjk <hjk@qt.io>2021-05-19 11:41:42 +0000
commit2db9ebc61504c5455f1fb2b4ed2a1cb6115ccbfa (patch)
treeb3fabec7fcf9d1df7f31ef71468dd120da9dffdc
parentb3ee704f175e853637c2784134693aadc9b31314 (diff)
downloadqt-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.cpp128
-rw-r--r--src/libs/utils/fileutils.h13
-rw-r--r--tests/auto/utils/fileutils/tst_fileutils.cpp72
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"