CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/File/AndroidContentURI.cpp
Views: 1401
#include "Common/File/AndroidContentURI.h"12bool AndroidContentURI::Parse(std::string_view path) {3const char *prefix = "content://";4if (!startsWith(path, prefix)) {5return false;6}78std::string_view components = path.substr(strlen(prefix));910std::vector<std::string_view> parts;11SplitString(components, '/', parts);12if (parts.size() == 3) {13// Single file URI.14provider = parts[0];15if (parts[1] == "tree") {16// Single directory URI.17// Not sure when we encounter these?18// file empty signals this type.19root = UriDecode(parts[2]);20return true;21} else if (parts[1] == "document") {22// root empty signals this type.23file = UriDecode(parts[2]);24return true;25} else {26// What's this?27return false;28}29} else if (parts.size() == 5) {30// Tree URI31provider = parts[0];32if (parts[1] != "tree") {33return false;34}35root = UriDecode(parts[2]);36if (parts[3] != "document") {37return false;38}39file = UriDecode(parts[4]);40// Sanity check file path.41return startsWith(file, root);42} else {43// Invalid Content URI44return false;45}46}4748AndroidContentURI AndroidContentURI::WithRootFilePath(const std::string &filePath) {49if (root.empty()) {50ERROR_LOG(Log::System, "WithRootFilePath cannot be used with single file URIs.");51return *this;52}5354AndroidContentURI uri = *this;55uri.file = uri.root;56if (!filePath.empty()) {57uri.file += "/" + filePath;58}59return uri;60}6162AndroidContentURI AndroidContentURI::WithComponent(std::string_view filePath) {63AndroidContentURI uri = *this;64if (uri.file.empty()) {65// Not sure what to do.66return uri;67}68if (uri.file.back() == ':') {69// Special case handling for Document URIs: Treat the ':' as a directory separator too (but preserved in the filename).70uri.file.append(filePath);71} else {72uri.file.push_back('/');73uri.file.append(filePath);74}75return uri;76}7778AndroidContentURI AndroidContentURI::WithExtraExtension(std::string_view extension) {79AndroidContentURI uri = *this;80uri.file.append(extension);81return uri;82}8384AndroidContentURI AndroidContentURI::WithReplacedExtension(const std::string &oldExtension, const std::string &newExtension) const {85_dbg_assert_(!oldExtension.empty() && oldExtension[0] == '.');86_dbg_assert_(!newExtension.empty() && newExtension[0] == '.');87AndroidContentURI uri = *this;88if (endsWithNoCase(file, oldExtension)) {89uri.file = file.substr(0, file.size() - oldExtension.size()) + newExtension;90}91return uri;92}9394AndroidContentURI AndroidContentURI::WithReplacedExtension(const std::string &newExtension) const {95_dbg_assert_(!newExtension.empty() && newExtension[0] == '.');96AndroidContentURI uri = *this;97if (file.empty()) {98return uri;99}100std::string extension = GetFileExtension();101uri.file = file.substr(0, file.size() - extension.size()) + newExtension;102return uri;103}104105bool AndroidContentURI::CanNavigateUp() const {106if (IsTreeURI()) {107return file.size() > root.size();108} else {109return file.find(':') != std::string::npos && file.back() != ':';110}111}112113// Only goes downwards in hierarchies. No ".." will ever be generated.114bool AndroidContentURI::ComputePathTo(const AndroidContentURI &other, std::string &path) const {115size_t offset = FilePath().size() + 1;116const auto &otherFilePath = other.FilePath();117if (offset >= otherFilePath.size()) {118ERROR_LOG(Log::System, "Bad call to PathTo. '%s' -> '%s'", FilePath().c_str(), other.FilePath().c_str());119return false;120}121122path = other.FilePath().substr(FilePath().size() + 1);123return true;124}125126std::string AndroidContentURI::GetFileExtension() const {127size_t pos = file.rfind('.');128if (pos == std::string::npos) {129return "";130}131size_t slash_pos = file.rfind('/');132if (slash_pos != std::string::npos && slash_pos > pos) {133// Don't want to detect "df/file" from "/as.df/file"134return "";135}136std::string ext = file.substr(pos);137for (size_t i = 0; i < ext.size(); i++) {138ext[i] = tolower(ext[i]);139}140return ext;141}142143std::string AndroidContentURI::GetLastPart() const {144if (file.empty()) {145// Can't do anything anyway.146return std::string();147}148149if (!CanNavigateUp()) {150size_t colon = file.rfind(':');151if (colon == std::string::npos) {152return std::string();153}154if (file.back() == ':') {155return file;156}157return file.substr(colon + 1);158}159160size_t slash = file.rfind('/');161if (slash == std::string::npos) {162// ok, look for the final colon. If it's the last char, we would have been caught above in !CanNavigateUp.163size_t colon = file.rfind(':');164if (colon == std::string::npos) {165return std::string();166}167return file.substr(colon + 1);168}169170std::string part = file.substr(slash + 1);171return part;172}173174bool AndroidContentURI::NavigateUp() {175if (!CanNavigateUp()) {176return false;177}178179size_t slash = file.rfind('/');180if (slash == std::string::npos) {181// ok, look for the final colon.182size_t colon = file.rfind(':');183if (colon == std::string::npos) {184return false;185}186file = file.substr(0, colon + 1); // Note: we include the colon in these paths.187return true;188}189190file = file.substr(0, slash);191return true;192}193194195std::string AndroidContentURI::ToString() const {196if (file.empty()) {197// Tree URI198return StringFromFormat("content://%s/tree/%s", provider.c_str(), UriEncode(root).c_str());199} else if (root.empty()) {200// Single file URI201return StringFromFormat("content://%s/document/%s", provider.c_str(), UriEncode(file).c_str());202} else {203// File URI from Tree204return StringFromFormat("content://%s/tree/%s/document/%s", provider.c_str(), UriEncode(root).c_str(), UriEncode(file).c_str());205}206}207208209