CmPath.h 4.8 KB

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