BsPath.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsPrerequisitesUtil.h"
  4. #include "BsException.h"
  5. namespace BansheeEngine
  6. {
  7. const Path Path::BLANK = Path();
  8. Path::Path()
  9. :mIsAbsolute(false)
  10. { }
  11. Path::Path(const WString& pathStr, PathType type)
  12. {
  13. assign(pathStr, type);
  14. }
  15. Path::Path(const String& pathStr, PathType type)
  16. {
  17. assign(pathStr, type);
  18. }
  19. Path::Path(wchar_t* pathStr, PathType type)
  20. {
  21. assign(pathStr);
  22. }
  23. Path::Path(const char* pathStr, PathType type)
  24. {
  25. assign(pathStr);
  26. }
  27. Path::Path(const Path& other)
  28. {
  29. assign(other);
  30. }
  31. Path& Path::operator= (const Path& path)
  32. {
  33. assign(path);
  34. return *this;
  35. }
  36. Path& Path::operator= (const WString& pathStr)
  37. {
  38. assign(pathStr);
  39. return *this;
  40. }
  41. Path& Path::operator= (const String& pathStr)
  42. {
  43. assign(pathStr);
  44. return *this;
  45. }
  46. Path& Path::operator= (const wchar_t* pathStr)
  47. {
  48. assign(pathStr);
  49. return *this;
  50. }
  51. Path& Path::operator= (const char* pathStr)
  52. {
  53. assign(pathStr);
  54. return *this;
  55. }
  56. void Path::swap(Path& path)
  57. {
  58. std::swap(mDirectories, path.mDirectories);
  59. std::swap(mFilename, path.mFilename);
  60. std::swap(mDevice, path.mDevice);
  61. std::swap(mNode, path.mNode);
  62. std::swap(mIsAbsolute, path.mIsAbsolute);
  63. }
  64. void Path::assign(const Path& path)
  65. {
  66. mDirectories = path.mDirectories;
  67. mFilename = path.mFilename;
  68. mDevice = path.mDevice;
  69. mNode = path.mNode;
  70. mIsAbsolute = path.mIsAbsolute;
  71. }
  72. void Path::assign(const WString& pathStr, PathType type)
  73. {
  74. assign(pathStr.data(), (UINT32)pathStr.length(), type);
  75. }
  76. void Path::assign(const String& pathStr, PathType type)
  77. {
  78. assign(pathStr.data(), (UINT32)pathStr.length(), type);
  79. }
  80. void Path::assign(const wchar_t* pathStr, PathType type)
  81. {
  82. assign(pathStr, (UINT32)wcslen(pathStr), type);
  83. }
  84. void Path::assign(const char* pathStr, PathType type)
  85. {
  86. assign(pathStr, (UINT32)strlen(pathStr), type);
  87. }
  88. void Path::assign(const wchar_t* pathStr, UINT32 numChars, PathType type)
  89. {
  90. switch (type)
  91. {
  92. case PathType::Windows:
  93. parseWindows(pathStr, numChars);
  94. break;
  95. case PathType::Unix:
  96. parseUnix(pathStr, numChars);
  97. break;
  98. default:
  99. #if BS_PLATFORM == BS_PLATFORM_WIN32
  100. parseWindows(pathStr, numChars);
  101. #elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX
  102. parseUnix(pathStr, numChars);
  103. #else
  104. static_assert(false, "Unsupported platform for path.");
  105. #endif
  106. break;
  107. }
  108. }
  109. void Path::assign(const char* pathStr, UINT32 numChars, PathType type)
  110. {
  111. switch (type)
  112. {
  113. case PathType::Windows:
  114. parseWindows(pathStr, numChars);
  115. break;
  116. case PathType::Unix:
  117. parseUnix(pathStr, numChars);
  118. break;
  119. default:
  120. #if BS_PLATFORM == BS_PLATFORM_WIN32
  121. parseWindows(pathStr, numChars);
  122. #elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX
  123. parseUnix(pathStr, numChars);
  124. #else
  125. static_assert(false, "Unsupported platform for path.");
  126. #endif
  127. break;
  128. }
  129. }
  130. WString Path::toWString(PathType type) const
  131. {
  132. switch (type)
  133. {
  134. case PathType::Windows:
  135. return buildWindows();
  136. case PathType::Unix:
  137. return buildUnix();
  138. default:
  139. #if BS_PLATFORM == BS_PLATFORM_WIN32
  140. return buildWindows();
  141. #elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX
  142. return buildUnix();
  143. #else
  144. static_assert(false, "Unsupported platform for path.");
  145. #endif
  146. break;
  147. }
  148. }
  149. String Path::toString(PathType type) const
  150. {
  151. switch (type)
  152. {
  153. case PathType::Windows:
  154. return BansheeEngine::toString(buildWindows());
  155. case PathType::Unix:
  156. return BansheeEngine::toString(buildUnix());
  157. default:
  158. #if BS_PLATFORM == BS_PLATFORM_WIN32
  159. return BansheeEngine::toString(buildWindows());
  160. #elif BS_PLATFORM == BS_PLATFORM_APPLE || BS_PLATFORM == BS_PLATFORM_LINUX
  161. return BansheeEngine::toString(buildUnix());
  162. #else
  163. static_assert(false, "Unsupported platform for path.");
  164. #endif
  165. break;
  166. }
  167. }
  168. Path Path::getParent() const
  169. {
  170. Path copy = *this;
  171. copy.makeParent();
  172. return copy;
  173. }
  174. Path Path::getAbsolute(const Path& base) const
  175. {
  176. Path copy = *this;
  177. copy.makeAbsolute(base);
  178. return copy;
  179. }
  180. Path Path::getRelative(const Path& base) const
  181. {
  182. Path copy = *this;
  183. copy.makeRelative(base);
  184. return copy;
  185. }
  186. Path Path::getDirectory() const
  187. {
  188. Path copy = *this;
  189. copy.mFilename.clear();
  190. return copy;
  191. }
  192. Path& Path::makeParent()
  193. {
  194. if (mFilename.empty())
  195. {
  196. if (mDirectories.empty())
  197. {
  198. if (!mIsAbsolute)
  199. mDirectories.push_back(L"..");
  200. }
  201. else
  202. {
  203. if (mDirectories.back() == L"..")
  204. mDirectories.push_back(L"..");
  205. else
  206. mDirectories.pop_back();
  207. }
  208. }
  209. else
  210. {
  211. mFilename.clear();
  212. }
  213. return *this;
  214. }
  215. Path& Path::makeAbsolute(const Path& base)
  216. {
  217. if (mIsAbsolute)
  218. return *this;
  219. Path absDir = base.getDirectory();
  220. if (base.isFile())
  221. absDir.pushDirectory(base.mFilename);
  222. for (auto& dir : mDirectories)
  223. absDir.pushDirectory(dir);
  224. absDir.setFilename(mFilename);
  225. *this = absDir;
  226. return *this;
  227. }
  228. Path& Path::makeRelative(const Path& base)
  229. {
  230. if (!base.includes(*this))
  231. return *this;
  232. mDirectories.erase(mDirectories.begin(), mDirectories.begin() + base.mDirectories.size());
  233. // Sometimes a directory name can be interpreted as a file and we're okay with that. Check for that
  234. // special case.
  235. if (base.isFile())
  236. {
  237. if (mDirectories.size() > 0)
  238. mDirectories.erase(mDirectories.begin());
  239. else
  240. mFilename = L"";
  241. }
  242. mDevice = L"";
  243. mNode = L"";
  244. mIsAbsolute = false;
  245. return *this;
  246. }
  247. bool Path::includes(const Path& child) const
  248. {
  249. if (mDevice != child.mDevice)
  250. return false;
  251. if (mNode != child.mNode)
  252. return false;
  253. auto iterParent = mDirectories.begin();
  254. auto iterChild = child.mDirectories.begin();
  255. for (; iterParent != mDirectories.end(); ++iterChild, ++iterParent)
  256. {
  257. if (iterChild == child.mDirectories.end())
  258. return false;
  259. if (!comparePathElem(*iterChild, *iterParent))
  260. return false;
  261. }
  262. if (!mFilename.empty())
  263. {
  264. if (iterChild == child.mDirectories.end())
  265. {
  266. if (child.mFilename.empty())
  267. return false;
  268. if (!comparePathElem(child.mFilename, mFilename))
  269. return false;
  270. }
  271. else
  272. {
  273. if (!comparePathElem(*iterChild, mFilename))
  274. return false;
  275. }
  276. }
  277. return true;
  278. }
  279. bool Path::equals(const Path& other) const
  280. {
  281. if (mIsAbsolute != other.mIsAbsolute)
  282. return false;
  283. if (mIsAbsolute)
  284. {
  285. if (!comparePathElem(mDevice, other.mDevice))
  286. return false;
  287. }
  288. if (mDirectories.size() != other.mDirectories.size())
  289. return false;
  290. if (!comparePathElem(mFilename, other.mFilename))
  291. return false;
  292. if (!comparePathElem(mNode, other.mNode))
  293. return false;
  294. auto iterMe = mDirectories.begin();
  295. auto iterOther = other.mDirectories.begin();
  296. for (; iterMe != mDirectories.end(); ++iterMe, ++iterOther)
  297. {
  298. if (!comparePathElem(*iterMe, *iterOther))
  299. return false;
  300. }
  301. return true;
  302. }
  303. Path& Path::append(const Path& path)
  304. {
  305. if (!mFilename.empty())
  306. pushDirectory(mFilename);
  307. for (auto& dir : path.mDirectories)
  308. pushDirectory(dir);
  309. mFilename = path.mFilename;
  310. return *this;
  311. }
  312. void Path::setBasename(const WString& basename)
  313. {
  314. mFilename = basename + getWExtension();
  315. }
  316. void Path::setBasename(const String& basename)
  317. {
  318. mFilename = BansheeEngine::toWString(basename) + getWExtension();
  319. }
  320. void Path::setExtension(const WString& extension)
  321. {
  322. WStringStream stream;
  323. stream << getWFilename(false);
  324. stream << extension;
  325. mFilename = stream.str();
  326. }
  327. void Path::setExtension(const String& extension)
  328. {
  329. setExtension(BansheeEngine::toWString(extension));
  330. }
  331. WString Path::getWFilename(bool extension) const
  332. {
  333. if (extension)
  334. return mFilename;
  335. else
  336. {
  337. WString::size_type pos = mFilename.rfind(L'.');
  338. if (pos != WString::npos)
  339. return mFilename.substr(0, pos);
  340. else
  341. return mFilename;
  342. }
  343. }
  344. String Path::getFilename(bool extension) const
  345. {
  346. return BansheeEngine::toString(getWFilename(extension));
  347. }
  348. WString Path::getWExtension() const
  349. {
  350. WString::size_type pos = mFilename.rfind(L'.');
  351. if (pos != WString::npos)
  352. return mFilename.substr(pos);
  353. else
  354. return WString();
  355. }
  356. String Path::getExtension() const
  357. {
  358. return BansheeEngine::toString(getWExtension());
  359. }
  360. const WString& Path::getWDirectory(UINT32 idx) const
  361. {
  362. if (idx >= (UINT32)mDirectories.size())
  363. {
  364. BS_EXCEPT(InvalidParametersException, "Index out of range: " + BansheeEngine::toString(idx) +
  365. ". Valid range: [0, " + BansheeEngine::toString((UINT32)mDirectories.size() - 1) + "]");
  366. }
  367. return mDirectories[idx];
  368. }
  369. String Path::getDirectory(UINT32 idx) const
  370. {
  371. return BansheeEngine::toString(getWDirectory(idx));
  372. }
  373. WString Path::getWTail(PathType type) const
  374. {
  375. if (isFile())
  376. return mFilename;
  377. else if (mDirectories.size() > 0)
  378. return mDirectories.back();
  379. else
  380. return StringUtil::WBLANK;
  381. }
  382. String Path::getTail(PathType type) const
  383. {
  384. return BansheeEngine::toString(getWTail(type));
  385. }
  386. void Path::clear()
  387. {
  388. mDirectories.clear();
  389. mDevice.clear();
  390. mFilename.clear();
  391. mNode.clear();
  392. mIsAbsolute = false;
  393. }
  394. void Path::throwInvalidPathException(const WString& path) const
  395. {
  396. BS_EXCEPT(InvalidParametersException, "Incorrectly formatted path provided: " + BansheeEngine::toString(path));
  397. }
  398. void Path::throwInvalidPathException(const String& path) const
  399. {
  400. BS_EXCEPT(InvalidParametersException, "Incorrectly formatted path provided: " + path);
  401. }
  402. WString Path::buildWindows() const
  403. {
  404. WStringStream result;
  405. if (!mNode.empty())
  406. {
  407. result << L"\\\\";
  408. result << mNode;
  409. result << L"\\";
  410. }
  411. else if (!mDevice.empty())
  412. {
  413. result << mDevice;
  414. result << L":\\";
  415. }
  416. else if (mIsAbsolute)
  417. {
  418. result << L"\\";
  419. }
  420. for (auto& dir : mDirectories)
  421. {
  422. result << dir;
  423. result << L"\\";
  424. }
  425. result << mFilename;
  426. return result.str();
  427. }
  428. WString Path::buildUnix() const
  429. {
  430. WStringStream result;
  431. auto dirIter = mDirectories.begin();
  432. if (!mDevice.empty())
  433. {
  434. result << L"/";
  435. result << mDevice;
  436. result << L":/";
  437. }
  438. else if (mIsAbsolute)
  439. {
  440. if (dirIter != mDirectories.end() && *dirIter == L"~")
  441. {
  442. result << L"~";
  443. dirIter++;
  444. }
  445. result << L"/";
  446. }
  447. for (; dirIter != mDirectories.end(); ++dirIter)
  448. {
  449. result << *dirIter;
  450. result << L"/";
  451. }
  452. result << mFilename;
  453. return result.str();
  454. }
  455. Path Path::operator+ (const Path& rhs) const
  456. {
  457. return Path::combine(*this, rhs);
  458. }
  459. Path& Path::operator+= (const Path& rhs)
  460. {
  461. return append(rhs);
  462. }
  463. bool Path::comparePathElem(const WString& left, const WString& right)
  464. {
  465. if (left.size() != right.size())
  466. return false;
  467. // TODO: Case sensitive/insensitive file path actually depends on used file-system but I'm not gonna check that
  468. for (UINT32 i = 0; i < (UINT32)left.size(); i++)
  469. {
  470. if (tolower(left[i]) != tolower(right[i]))
  471. return false;
  472. }
  473. return true;
  474. }
  475. Path Path::combine(const Path& left, const Path& right)
  476. {
  477. Path output = left;
  478. return output.append(right);
  479. }
  480. void Path::pushDirectory(const WString& dir)
  481. {
  482. if (!dir.empty() && dir != L".")
  483. {
  484. if (dir == L"..")
  485. {
  486. if (!mDirectories.empty() && mDirectories.back() != L"..")
  487. mDirectories.pop_back();
  488. else
  489. mDirectories.push_back(dir);
  490. }
  491. else
  492. mDirectories.push_back(dir);
  493. }
  494. }
  495. void Path::pushDirectory(const String& dir)
  496. {
  497. pushDirectory(BansheeEngine::toWString(dir));
  498. }
  499. }