#include "BsPrerequisitesUtil.h" #include "BsException.h" namespace BansheeEngine { const Path Path::BLANK = Path(); Path::Path() :mIsAbsolute(false) { } Path::Path(const WString& pathStr, PathType type) { assign(pathStr, type); } Path::Path(const String& pathStr, PathType type) { assign(pathStr, type); } Path::Path(wchar_t* pathStr, PathType type) { assign(pathStr); } Path::Path(const char* pathStr, PathType type) { assign(pathStr); } Path::Path(const Path& other) { assign(other); } Path& Path::operator= (const Path& path) { assign(path); return *this; } Path& Path::operator= (const WString& pathStr) { assign(pathStr); return *this; } Path& Path::operator= (const String& pathStr) { assign(pathStr); return *this; } Path& Path::operator= (const wchar_t* pathStr) { assign(pathStr); return *this; } Path& Path::operator= (const char* pathStr) { assign(pathStr); return *this; } void Path::swap(Path& path) { std::swap(mDirectories, path.mDirectories); std::swap(mFilename, path.mFilename); std::swap(mDevice, path.mDevice); std::swap(mNode, path.mNode); std::swap(mIsAbsolute, path.mIsAbsolute); } void Path::assign(const Path& path) { mDirectories = path.mDirectories; mFilename = path.mFilename; mDevice = path.mDevice; mNode = path.mNode; mIsAbsolute = path.mIsAbsolute; } void Path::assign(const WString& pathStr, PathType type) { assign(pathStr.data(), (UINT32)pathStr.length(), type); } void Path::assign(const String& pathStr, PathType type) { assign(pathStr.data(), (UINT32)pathStr.length(), type); } void Path::assign(const wchar_t* pathStr, PathType type) { assign(pathStr, (UINT32)wcslen(pathStr), type); } void Path::assign(const char* pathStr, PathType type) { assign(pathStr, (UINT32)strlen(pathStr), type); } void Path::assign(const wchar_t* pathStr, UINT32 numChars, PathType type) { switch (type) { case PathType::Windows: parseWindows(pathStr, numChars); break; case PathType::Unix: parseUnix(pathStr, numChars); break; default: #if BS_PLATFORM == BS_PLATFORM_WIN32 parseWindows(pathStr, numChars); #elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX parseUnix(pathStr, numChars); #else static_assert(false, "Unsupported platform for path."); #endif break; } } void Path::assign(const char* pathStr, UINT32 numChars, PathType type) { switch (type) { case PathType::Windows: parseWindows(pathStr, numChars); break; case PathType::Unix: parseUnix(pathStr, numChars); break; default: #if BS_PLATFORM == BS_PLATFORM_WIN32 parseWindows(pathStr, numChars); #elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX parseUnix(pathStr, numChars); #else static_assert(false, "Unsupported platform for path."); #endif break; } } WString Path::toWString(PathType type) const { switch (type) { case PathType::Windows: return buildWindows(); case PathType::Unix: return buildUnix(); default: #if BS_PLATFORM == BS_PLATFORM_WIN32 return buildWindows(); #elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX return buildUnix(); #else static_assert(false, "Unsupported platform for path."); #endif break; } } String Path::toString(PathType type) const { switch (type) { case PathType::Windows: return BansheeEngine::toString(buildWindows()); case PathType::Unix: return BansheeEngine::toString(buildUnix()); default: #if BS_PLATFORM == BS_PLATFORM_WIN32 return BansheeEngine::toString(buildWindows()); #elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX return BansheeEngine::toString(buildUnix()); #else static_assert(false, "Unsupported platform for path."); #endif break; } } Path Path::getParent() const { Path copy = *this; copy.makeParent(); return copy; } Path Path::getAbsolute(const Path& base) const { Path copy = *this; copy.makeAbsolute(base); return copy; } Path Path::getRelative(const Path& base) const { Path copy = *this; copy.makeRelative(base); return copy; } Path Path::getDirectory() const { Path copy = *this; copy.mFilename.clear(); return copy; } Path& Path::makeParent() { if (mFilename.empty()) { if (mDirectories.empty()) { if (!mIsAbsolute) mDirectories.push_back(L".."); } else { if (mDirectories.back() == L"..") mDirectories.push_back(L".."); else mDirectories.pop_back(); } } else { mFilename.clear(); } return *this; } Path& Path::makeAbsolute(const Path& base) { if (mIsAbsolute) return *this; Path absDir = base.getDirectory(); if (base.isFile()) absDir.pushDirectory(base.mFilename); for (auto& dir : mDirectories) absDir.pushDirectory(dir); absDir.setFilename(mFilename); *this = absDir; return *this; } Path& Path::makeRelative(const Path& base) { if (!base.includes(*this)) return *this; mDirectories.erase(mDirectories.begin(), mDirectories.begin() + base.mDirectories.size()); mDevice = L""; mNode = L""; mIsAbsolute = false; return *this; } bool Path::includes(const Path& child) const { if (mDevice != child.mDevice) return false; if (mNode != child.mNode) return false; auto iterParent = mDirectories.begin(); auto iterChild = child.mDirectories.begin(); for (; iterParent != mDirectories.end(); ++iterChild, ++iterParent) { if (iterChild == child.mDirectories.end()) return false; if (!comparePathElem(*iterChild, *iterParent)) return false; } if (!mFilename.empty()) { if (iterChild == child.mDirectories.end()) { if (child.mFilename.empty()) return false; if (!comparePathElem(child.mFilename, mFilename)) return false; } else { if (!comparePathElem(*iterChild, mFilename)) return false; } } return true; } bool Path::equals(const Path& other) const { if (mIsAbsolute != other.mIsAbsolute) return false; if (mIsAbsolute) { if (!comparePathElem(mDevice, other.mDevice)) return false; } if (mDirectories.size() != other.mDirectories.size()) return false; if (!comparePathElem(mFilename, other.mFilename)) return false; if (!comparePathElem(mNode, other.mNode)) return false; auto iterMe = mDirectories.begin(); auto iterOther = other.mDirectories.begin(); for (; iterMe != mDirectories.end(); ++iterMe, ++iterOther) { if (!comparePathElem(*iterMe, *iterOther)) return false; } return true; } Path& Path::append(const Path& path) { if (!mFilename.empty()) pushDirectory(mFilename); for (auto& dir : path.mDirectories) pushDirectory(dir); mFilename = path.mFilename; return *this; } void Path::setBasename(const WString& basename) { mFilename = basename + getWExtension(); } void Path::setBasename(const String& basename) { mFilename = BansheeEngine::toWString(basename) + getWExtension(); } void Path::setExtension(const WString& extension) { WStringStream stream; stream << getWFilename(false); stream << extension; mFilename = stream.str(); } void Path::setExtension(const String& extension) { setExtension(BansheeEngine::toWString(extension)); } WString Path::getWFilename(bool extension) const { if (extension) return mFilename; else { WString::size_type pos = mFilename.rfind(L'.'); if (pos != WString::npos) return mFilename.substr(0, pos); else return mFilename; } } String Path::getFilename(bool extension) const { return BansheeEngine::toString(getWFilename(extension)); } WString Path::getWExtension() const { WString::size_type pos = mFilename.rfind(L'.'); if (pos != WString::npos) return mFilename.substr(pos); else return WString(); } String Path::getExtension() const { return BansheeEngine::toString(getWExtension()); } const WString& Path::getWDirectory(UINT32 idx) const { if (idx >= (UINT32)mDirectories.size()) { BS_EXCEPT(InvalidParametersException, "Index out of range: " + BansheeEngine::toString(idx) + ". Valid range: [0, " + BansheeEngine::toString((UINT32)mDirectories.size() - 1) + "]"); } return mDirectories[idx]; } String Path::getDirectory(UINT32 idx) const { return BansheeEngine::toString(getWDirectory(idx)); } WString Path::getWTail(PathType type) const { if (isFile()) return mFilename; else if (mDirectories.size() > 0) return mDirectories.back(); else return StringUtil::WBLANK; } String Path::getTail(PathType type) const { return BansheeEngine::toString(getWTail(type)); } void Path::clear() { mDirectories.clear(); mDevice.clear(); mFilename.clear(); mNode.clear(); mIsAbsolute = false; } void Path::throwInvalidPathException(const WString& path) const { BS_EXCEPT(InvalidParametersException, "Incorrectly formatted path provided: " + BansheeEngine::toString(path)); } void Path::throwInvalidPathException(const String& path) const { BS_EXCEPT(InvalidParametersException, "Incorrectly formatted path provided: " + path); } WString Path::buildWindows() const { WStringStream result; if (!mNode.empty()) { result << L"\\\\"; result << mNode; result << L"\\"; } else if (!mDevice.empty()) { result << mDevice; result << L":\\"; } else if (mIsAbsolute) { result << L"\\"; } for (auto& dir : mDirectories) { result << dir; result << L"\\"; } result << mFilename; return result.str(); } WString Path::buildUnix() const { WStringStream result; auto dirIter = mDirectories.begin(); if (!mDevice.empty()) { result << L"/"; result << mDevice; result << L":/"; } else if (mIsAbsolute) { if (dirIter != mDirectories.end() && *dirIter == L"~") { result << L"~"; dirIter++; } result << L"/"; } for (; dirIter != mDirectories.end(); ++dirIter) { result << *dirIter; result << L"/"; } result << mFilename; return result.str(); } Path Path::operator+ (const Path& rhs) const { return Path::combine(*this, rhs); } Path& Path::operator+= (const Path& rhs) { return append(rhs); } bool Path::comparePathElem(const WString& left, const WString& right) { if (left.size() != right.size()) return false; // TODO: Case sensitive/insensitive file path actually depends on used file-system but I'm not gonna check that for (UINT32 i = 0; i < (UINT32)left.size(); i++) { if (tolower(left[i]) != tolower(right[i])) return false; } return true; } Path Path::combine(const Path& left, const Path& right) { Path output = left; return output.append(right); } void Path::pushDirectory(const WString& dir) { if (!dir.empty() && dir != L".") { if (dir == L"..") { if (!mDirectories.empty() && mDirectories.back() != L"..") mDirectories.pop_back(); else mDirectories.push_back(dir); } else mDirectories.push_back(dir); } } void Path::pushDirectory(const String& dir) { pushDirectory(BansheeEngine::toWString(dir)); } }