CmPath.h 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #pragma once
  2. #include "CmPrerequisitesUtil.h"
  3. #include <boost/filesystem.hpp>
  4. namespace BansheeEngine
  5. {
  6. /**
  7. * @brief Various string manipulations of file paths.
  8. */
  9. class CM_UTILITY_EXPORT Path
  10. {
  11. public:
  12. /**
  13. * @brief Returns file extension extracted from the provided
  14. * path, with a leading ".".
  15. */
  16. static WString getExtension(const WString& path)
  17. {
  18. boost::filesystem3::wpath ext = boost::filesystem3::extension(boost::filesystem3::wpath(path.c_str()));
  19. return ext.wstring().c_str();
  20. }
  21. /**
  22. * @brief Query if a path has the specified extension. Provided
  23. * extension must contain the leading ".".
  24. */
  25. static bool hasExtension(const WString& path, const WString& extension)
  26. {
  27. return getExtension(path) == extension;
  28. }
  29. /**
  30. * @brief Replaces or adds an extension on a file path. Provided
  31. * extension must contain the leading ".".
  32. */
  33. static void replaceExtension(WString& path, const WString& newExtension)
  34. {
  35. boost::filesystem3::path internalPath = path.c_str();
  36. path = internalPath.replace_extension(newExtension.c_str()).c_str();
  37. }
  38. /**
  39. * @brief Returns a path that is one level higher than the provided path, unless the path
  40. * is already at the root. Otherwise returns the initial path.
  41. */
  42. static WString parentPath(const WString& path)
  43. {
  44. boost::filesystem3::path internalPath = path.c_str();
  45. return internalPath.parent_path().c_str();
  46. }
  47. /**
  48. * @brief Returns true if path child is included in path parent.
  49. * Both paths must be canonical.
  50. */
  51. static bool includes(const WString& child, const WString& parent)
  52. {
  53. Vector<WString>::type childPathElems = split(child);
  54. Vector<WString>::type parentPathElems = split(parent);
  55. auto iterChild = childPathElems.begin();
  56. auto iterParent = parentPathElems.begin();
  57. for(; iterParent != parentPathElems.end(); ++iterChild, ++iterParent)
  58. {
  59. if(iterChild == childPathElems.end())
  60. return false;
  61. if(!comparePathElements(*iterChild, *iterParent))
  62. return false;
  63. }
  64. return true;
  65. }
  66. /**
  67. * @brief Returns path relative to base.
  68. */
  69. static WString relative(const WString& base, const WString& path)
  70. {
  71. Vector<WString>::type basePathElems = split(base);
  72. Vector<WString>::type pathElems = split(path);
  73. auto iterBase = basePathElems.begin();
  74. auto iterPath = pathElems.begin();
  75. for(; iterBase != basePathElems.end(); ++iterBase, ++iterPath)
  76. {
  77. if(!comparePathElements(*iterBase, *iterPath))
  78. return L"";
  79. }
  80. WString relativePath;
  81. for(; iterPath != pathElems.end(); ++iterPath)
  82. {
  83. relativePath = Path::combine(relativePath, *iterPath);
  84. }
  85. return relativePath;
  86. }
  87. /**
  88. * @brief Splits a path into string entries. Path separator
  89. * may be "\" or "/". Path separator will not be included
  90. * in the returned strings.
  91. */
  92. static Vector<WString>::type split(const WString& path)
  93. {
  94. Vector<WString>::type splitPath;
  95. WString standardizedPath = standardizePath(path);
  96. return StringUtil::split(standardizedPath, L"/");
  97. }
  98. /**
  99. * @brief Combines two paths using the "/" path separator.
  100. */
  101. static WString combine(const WString& base, const WString& name)
  102. {
  103. if (base.empty())
  104. return name;
  105. else
  106. return base + L'/' + name;
  107. }
  108. /**
  109. * @brief Compares two canonical paths and returns true if they are equal.
  110. */
  111. static bool equals(const WString& left, const WString& right)
  112. {
  113. Vector<WString>::type leftElements = split(left);
  114. Vector<WString>::type rightElements = split(right);
  115. UINT32 idx = 0;
  116. for(auto& leftElem : leftElements)
  117. {
  118. if(leftElem.empty())
  119. continue;
  120. while(idx < (UINT32)rightElements.size() && rightElements[idx].empty())
  121. idx++;
  122. if(idx >= (UINT32)rightElements.size())
  123. return false; // Right path is deeper than left path
  124. if(!comparePathElements(leftElem, rightElements[idx]))
  125. return false;
  126. idx++;
  127. }
  128. while(idx < (UINT32)rightElements.size() && rightElements[idx].empty())
  129. idx++;
  130. if(idx < (UINT32)rightElements.size())
  131. return false; // Left path is deeper than right path
  132. return true;
  133. }
  134. /**
  135. * @brief Compares two path elements for equality.
  136. *
  137. * @note Should not be used for comparing entire paths. First you need to split your
  138. * path into sub-elements using some other method and then send those sub-elements to
  139. * this method.
  140. */
  141. static bool comparePathElements(const WString& left, const WString& right)
  142. {
  143. if(left.size() != right.size())
  144. return false;
  145. for(UINT32 i = 0; i < (UINT32)left.size(); i++)
  146. {
  147. #if CM_PLATFORM == CM_PLATFORM_WIN32 // Compare case insensitive
  148. if(tolower(left[i]) != tolower(right[i]))
  149. return false;
  150. #else
  151. assert(false); // Implement case sensitive or insensitive comparison, depending on platform
  152. #endif
  153. }
  154. return true;
  155. }
  156. /**
  157. * @brief Extracts filename from the provided path.
  158. *
  159. * @param path Path to the file.
  160. * @param includeExtension (optional) If true, filename extension will be included in the returned string.
  161. */
  162. static WString getFilename(const WString& path, bool includeExtension = true)
  163. {
  164. boost::filesystem3::path internalPath = path.c_str();
  165. if(includeExtension)
  166. return internalPath.filename().c_str();
  167. else
  168. return internalPath.stem().c_str();
  169. }
  170. /**
  171. * @brief Method for standardizing paths - use forward slashes only, end without a slash.
  172. */
  173. static WString standardizePath(const WString& inPath)
  174. {
  175. WString path = inPath;
  176. std::replace(path.begin(), path.end(), L'\\', L'/');
  177. while(path.length() > 0 && path.back() == L'/')
  178. path.pop_back();
  179. return path;
  180. }
  181. };
  182. }