forked from qt-creator/qt-creator
Extend Utils::FilePath for relative paths
The new methods allows converting a path to a file or directory into a path relative to another path to a file or directory. Change-Id: I8c743d5bced9fec81b05ce94ac2b7bec307d9028 Reviewed-by: André Hartmann <aha_1980@gmx.de> Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -44,6 +44,7 @@ private:
|
||||
public:
|
||||
static QString convertFileNameToElementName(const QString &fileName);
|
||||
static QString convertElementNameToBaseFileName(const QString &elementName);
|
||||
// TODO use Utils::FilePath instead
|
||||
static QString calcRelativePath(const QString &absoluteFileName, const QString &anchorPath);
|
||||
static QString calcElementNameSearchId(const QString &elementName);
|
||||
static QStringList buildElementsPath(const QString &filePath, bool ignoreLastFilePathPart);
|
||||
|
@@ -821,6 +821,15 @@ FilePath FilePath::absolutePath() const
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Constructs an absolute FilePath from this path which
|
||||
/// is interpreted as being relative to \a anchor.
|
||||
FilePath FilePath::absoluteFromRelativePath(const FilePath &anchor) const
|
||||
{
|
||||
QDir anchorDir = QFileInfo(anchor.m_data).absoluteDir();
|
||||
QString absoluteFilePath = QFileInfo(anchorDir, m_data).canonicalFilePath();
|
||||
return FilePath::fromString(absoluteFilePath);
|
||||
}
|
||||
|
||||
/// Constructs a FilePath from \a filename
|
||||
/// \a filename is not checked for validity.
|
||||
FilePath FilePath::fromString(const QString &filename)
|
||||
@@ -970,6 +979,94 @@ FilePath FilePath::relativeChildPath(const FilePath &parent) const
|
||||
return FilePath::fromString(m_data.mid(parent.m_data.size() + 1, -1));
|
||||
}
|
||||
|
||||
/// \returns the relativePath of FilePath to given \a anchor.
|
||||
/// Both, FilePath and anchor may be files or directories.
|
||||
/// Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// FilePath filePath("/foo/b/ar/file.txt");
|
||||
/// FilePath relativePath = filePath.relativePath("/foo/c");
|
||||
/// qDebug() << relativePath
|
||||
/// \endcode
|
||||
///
|
||||
/// The debug output will be "../b/ar/file.txt".
|
||||
///
|
||||
FilePath FilePath::relativePath(const FilePath &anchor) const
|
||||
{
|
||||
const QFileInfo fileInfo(m_data);
|
||||
QString absolutePath;
|
||||
QString filename;
|
||||
if (fileInfo.isFile()) {
|
||||
absolutePath = fileInfo.absolutePath();
|
||||
filename = fileInfo.fileName();
|
||||
} else if (fileInfo.isDir()) {
|
||||
absolutePath = fileInfo.absoluteFilePath();
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
const QFileInfo anchorInfo(anchor.m_data);
|
||||
QString absoluteAnchorPath;
|
||||
if (anchorInfo.isFile())
|
||||
absoluteAnchorPath = anchorInfo.absolutePath();
|
||||
else if (anchorInfo.isDir())
|
||||
absoluteAnchorPath = anchorInfo.absoluteFilePath();
|
||||
else
|
||||
return {};
|
||||
QString relativeFilePath = calcRelativePath(absolutePath, absoluteAnchorPath);
|
||||
if (!filename.isEmpty()) {
|
||||
if (!relativeFilePath.isEmpty())
|
||||
relativeFilePath += '/';
|
||||
relativeFilePath += filename;
|
||||
}
|
||||
return FilePath::fromString(relativeFilePath);
|
||||
}
|
||||
|
||||
/// \returns the relativePath of \a absolutePath to given \a absoluteAnchorPath.
|
||||
/// Both paths must be an absolute path to a directory. Example usage:
|
||||
///
|
||||
/// \code
|
||||
/// qDebug() << FilePath::calcRelativePath("/foo/b/ar", "/foo/c");
|
||||
/// \endcode
|
||||
///
|
||||
/// The debug output will be "../b/ar".
|
||||
///
|
||||
/// \see FilePath::relativePath
|
||||
///
|
||||
QString FilePath::calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath)
|
||||
{
|
||||
if (absolutePath.isEmpty() || absoluteAnchorPath.isEmpty())
|
||||
return QString();
|
||||
// TODO using split() instead of parsing the strings by char index is slow
|
||||
// and needs more memory (but the easiest implementation for now)
|
||||
const QStringList splits1 = absolutePath.split('/');
|
||||
const QStringList splits2 = absoluteAnchorPath.split('/');
|
||||
int i = 0;
|
||||
while (i < splits1.count() && i < splits2.count() && splits1.at(i) == splits2.at(i))
|
||||
++i;
|
||||
QString relativePath;
|
||||
int j = i;
|
||||
bool addslash = false;
|
||||
while (j < splits2.count()) {
|
||||
if (!splits2.at(j).isEmpty()) {
|
||||
if (addslash)
|
||||
relativePath += '/';
|
||||
relativePath += "..";
|
||||
addslash = true;
|
||||
}
|
||||
++j;
|
||||
}
|
||||
while (i < splits1.count()) {
|
||||
if (!splits1.at(i).isEmpty()) {
|
||||
if (addslash)
|
||||
relativePath += '/';
|
||||
relativePath += splits1.at(i);
|
||||
addslash = true;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
FilePath FilePath::pathAppended(const QString &str) const
|
||||
{
|
||||
FilePath fn = *this;
|
||||
|
@@ -61,6 +61,9 @@ extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
// tst_fileutils becomes a friend of Utils::FilePath for testing private method
|
||||
class tst_fileutils;
|
||||
|
||||
namespace Utils {
|
||||
|
||||
class QTCREATOR_UTILS_EXPORT FilePath
|
||||
@@ -89,6 +92,7 @@ public:
|
||||
|
||||
FilePath parentDir() const;
|
||||
FilePath absolutePath() const;
|
||||
FilePath absoluteFromRelativePath(const FilePath &anchor) const;
|
||||
|
||||
bool operator==(const FilePath &other) const;
|
||||
bool operator!=(const FilePath &other) const;
|
||||
@@ -107,6 +111,7 @@ public:
|
||||
bool isNewerThan(const QDateTime &timeStamp) const;
|
||||
|
||||
FilePath relativeChildPath(const FilePath &parent) const;
|
||||
FilePath relativePath(const FilePath &anchor) const;
|
||||
FilePath pathAppended(const QString &str) const;
|
||||
FilePath stringAppended(const QString &str) const;
|
||||
FilePath resolvePath(const QString &fileName) const;
|
||||
@@ -126,6 +131,9 @@ public:
|
||||
QUrl toUrl() const;
|
||||
|
||||
private:
|
||||
friend class ::tst_fileutils;
|
||||
static QString calcRelativePath(const QString &absolutePath, const QString &absoluteAnchorPath);
|
||||
|
||||
QString m_data;
|
||||
QUrl m_url;
|
||||
};
|
||||
|
@@ -38,14 +38,47 @@ class tst_fileutils : public QObject
|
||||
public:
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void parentDir_data();
|
||||
void parentDir();
|
||||
void isChildOf_data();
|
||||
void isChildOf();
|
||||
void fileName_data();
|
||||
void fileName();
|
||||
void calcRelativePath_data();
|
||||
void calcRelativePath();
|
||||
void relativePath_specials();
|
||||
void relativePath_data();
|
||||
void relativePath();
|
||||
|
||||
private:
|
||||
QTemporaryDir tempDir;
|
||||
QString rootPath;
|
||||
};
|
||||
|
||||
static void touch(const QDir &dir, const QString &filename)
|
||||
{
|
||||
QFile file(dir.absoluteFilePath(filename));
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void tst_fileutils::initTestCase()
|
||||
{
|
||||
// initialize test for tst_fileutiles::relativePath*()
|
||||
QVERIFY(tempDir.isValid());
|
||||
rootPath = tempDir.path();
|
||||
QDir dir(rootPath);
|
||||
dir.mkpath("a/b/c/d");
|
||||
dir.mkpath("a/x/y/z");
|
||||
dir.mkpath("a/b/x/y/z");
|
||||
dir.mkpath("x/y/z");
|
||||
touch(dir, "a/b/c/d/file1.txt");
|
||||
touch(dir, "a/x/y/z/file2.txt");
|
||||
touch(dir, "a/file3.txt");
|
||||
touch(dir, "x/y/file4.txt");
|
||||
}
|
||||
|
||||
void tst_fileutils::parentDir_data()
|
||||
{
|
||||
QTest::addColumn<QString>("path");
|
||||
@@ -164,5 +197,65 @@ void tst_fileutils::fileName()
|
||||
QCOMPARE(FilePath::fromString(path).fileNameWithPathComponents(components), result);
|
||||
}
|
||||
|
||||
void tst_fileutils::calcRelativePath_data()
|
||||
{
|
||||
QTest::addColumn<QString>("absolutePath");
|
||||
QTest::addColumn<QString>("anchorPath");
|
||||
QTest::addColumn<QString>("result");
|
||||
|
||||
QTest::newRow("empty") << "" << "" << "";
|
||||
QTest::newRow("leftempty") << "" << "/" << "";
|
||||
QTest::newRow("rightempty") << "/" << "" << "";
|
||||
QTest::newRow("root") << "/" << "/" << "";
|
||||
QTest::newRow("simple1") << "/a" << "/" << "a";
|
||||
QTest::newRow("simple2") << "/" << "/a" << "..";
|
||||
QTest::newRow("simple3") << "/a" << "/a" << "";
|
||||
QTest::newRow("extraslash1") << "/a/b/c" << "/a/b/c" << "";
|
||||
QTest::newRow("extraslash2") << "/a/b/c" << "/a/b/c/" << "";
|
||||
QTest::newRow("extraslash3") << "/a/b/c/" << "/a/b/c" << "";
|
||||
QTest::newRow("normal1") << "/a/b/c" << "/a/x" << "../b/c";
|
||||
QTest::newRow("normal2") << "/a/b/c" << "/a/x/y" << "../../b/c";
|
||||
QTest::newRow("normal3") << "/a/b/c" << "/x/y" << "../../a/b/c";
|
||||
}
|
||||
|
||||
void tst_fileutils::calcRelativePath()
|
||||
{
|
||||
QFETCH(QString, absolutePath);
|
||||
QFETCH(QString, anchorPath);
|
||||
QFETCH(QString, result);
|
||||
QString relativePath = Utils::FilePath::calcRelativePath(absolutePath, anchorPath);
|
||||
QCOMPARE(relativePath, result);
|
||||
}
|
||||
|
||||
void tst_fileutils::relativePath_specials()
|
||||
{
|
||||
QString path = FilePath::fromString("").relativePath(FilePath::fromString("")).toString();
|
||||
QCOMPARE(path, "");
|
||||
}
|
||||
|
||||
void tst_fileutils::relativePath_data()
|
||||
{
|
||||
QTest::addColumn<QString>("relative");
|
||||
QTest::addColumn<QString>("anchor");
|
||||
QTest::addColumn<QString>("result");
|
||||
|
||||
QTest::newRow("samedir") << "/" << "/" << "";
|
||||
QTest::newRow("dir2dir_1") << "a/b/c/d" << "a/x/y/z" << "../../../b/c/d";
|
||||
QTest::newRow("dir2dir_2") << "a/b" <<"a/b/c" << "..";
|
||||
QTest::newRow("file2file_1") << "a/b/c/d/file1.txt" << "a/file3.txt" << "b/c/d/file1.txt";
|
||||
QTest::newRow("dir2file_1") << "a/b/c" << "a/x/y/z/file2.txt" << "../../../b/c";
|
||||
QTest::newRow("file2dir_1") << "a/b/c/d/file1.txt" << "x/y" << "../../a/b/c/d/file1.txt";
|
||||
}
|
||||
|
||||
void tst_fileutils::relativePath()
|
||||
{
|
||||
QFETCH(QString, relative);
|
||||
QFETCH(QString, anchor);
|
||||
QFETCH(QString, result);
|
||||
FilePath actualPath = FilePath::fromString(rootPath + "/" + relative)
|
||||
.relativePath(FilePath::fromString(rootPath + "/" + anchor));
|
||||
QCOMPARE(actualPath.toString(), result);
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_fileutils)
|
||||
#include "tst_fileutils.moc"
|
||||
|
Reference in New Issue
Block a user