/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "StringHelper.h" #include #include #include #include #include #include #include #define UPPERCASE "[A-Z0-9]" #define LOWERCASE "[a-z][a-z0-9]*" #define NUMCASE "[0-9]" #define CAPCASE "[A-Z][a-z][a-z0-9]*" static const std::regex kStartUppercase("^" UPPERCASE); static const std::regex kStartLowercase("^" LOWERCASE); static const std::regex kStartCapcase("^" CAPCASE); static const std::regex kStartNumcase("^" CAPCASE); namespace android { std::string StringHelper::Uppercase(const std::string &in) { std::string out{in}; for (auto &ch : out) { ch = toupper(ch); } return out; } std::string StringHelper::Lowercase(const std::string &in) { std::string out{in}; for (auto &ch : out) { ch = tolower(ch); } return out; } std::string StringHelper::Capitalize(const std::string &in) { std::string out{in}; if(!out.empty()) { out[0] = toupper(out[0]); } return out; } // Combines multiple single character upper case tokens together // {"U", "I", "Error"} becomes {"UI", "Error"} static void combineSingleCharTokens(const std::vector& from, std::vector* to) { std::string current; for (const std::string& str : from) { if (str.size() == 1 && (isupper(str[0]) || isdigit(str[0]))) { current += str; } else { if (!current.empty()) { to->push_back(current); current = ""; } to->push_back(str); } } if (!current.empty()) to->push_back(current); } // Tokenizes strings first based on "_"s and then based on case // PascalCase (CAPCASE) regex is given the highest priority and the remaining uppercase characters // are grouped together. Digits are added to the preceding group, whichever it may be. // Ipv4Addr => {"Ipv4", "Addr"}, V3Bool => {"V3", "Bool"} void StringHelper::Tokenize(const std::string& in, std::vector* vec) { vec->clear(); if (in.empty()) return; if (in.find('_') != std::string::npos) { std::vector snakeCaseComponents; SplitString(in, '_', &snakeCaseComponents); for (const std::string& comp : snakeCaseComponents) { std::vector tokens; Tokenize(comp, &tokens); vec->insert(vec->end(), tokens.begin(), tokens.end()); } return; } std::smatch match; std::string copy(in); std::vector matches; std::vector tmpVec; while (!copy.empty()) { if (std::regex_search(copy, match, kStartLowercase)) matches.push_back(match.str(0)); if (std::regex_search(copy, match, kStartCapcase)) matches.push_back(match.str(0)); if (std::regex_search(copy, match, kStartUppercase)) matches.push_back(match.str(0)); if (std::regex_search(copy, match, kStartNumcase)) matches.push_back(match.str(0)); if (!matches.empty()) { std::string& maxmatch = matches[0]; for (std::string& match : matches) if (match.length() > maxmatch.length()) maxmatch = match; tmpVec.push_back(maxmatch); copy = copy.substr(maxmatch.length()); matches.clear(); } else { LOG(WARNING) << "Could not stylize \"" << in << "\""; // don't know what to do, so push back the rest of the string. tmpVec.push_back(copy); } } combineSingleCharTokens(tmpVec, vec); } std::string StringHelper::ToCamelCase(const std::string &in) { std::vector components; Tokenize(in, &components); if (components.empty()) { if (!in.empty()) LOG(WARNING) << "Could not stylize \"" << in << "\""; return in; } components[0] = Lowercase(components[0]); for (size_t i = 1; i < components.size(); i++) { components[i] = Capitalize(components[i]); } return JoinStrings(components, ""); } std::string StringHelper::ToPascalCase(const std::string &in) { std::vector components; Tokenize(in, &components); for (size_t i = 0; i < components.size(); i++) { components[i] = Capitalize(components[i]); } return JoinStrings(components, ""); } std::string StringHelper::ToUpperSnakeCase(const std::string &in) { std::vector components; Tokenize(in, &components); for (size_t i = 0; i < components.size(); i++) { components[i] = Uppercase(components[i]); } return JoinStrings(components, "_"); } std::string StringHelper::ToLowerSnakeCase(const std::string &in) { std::vector components; Tokenize(in, &components); for (size_t i = 0; i < components.size(); i++) { components[i] = Lowercase(components[i]); } return JoinStrings(components, "_"); } std::string StringHelper::ToCase(StringHelper::Case c, const std::string &in) { switch(c) { case kCamelCase: return ToCamelCase(in); case kPascalCase: return ToPascalCase(in); case kUpperSnakeCase: return ToUpperSnakeCase(in); case kLowerSnakeCase: return ToLowerSnakeCase(in); case kNoCase: return in; } LOG(FATAL) << "Should not reach here."; return in; } bool StringHelper::EndsWith(const std::string &in, const std::string &suffix) { return in.size() >= suffix.size() && in.substr(in.size() - suffix.size()) == suffix; } bool StringHelper::StartsWith(const std::string &in, const std::string &prefix) { return in.size() >= prefix.size() && in.substr(0, prefix.size()) == prefix; } std::string StringHelper::RTrim(const std::string &in, const std::string &suffix) { if (EndsWith(in, suffix)) { return in.substr(0, in.size() - suffix.size()); } return in; } std::string StringHelper::LTrim(const std::string &in, const std::string &prefix) { if (StartsWith(in, prefix)) { return in.substr(prefix.size()); } return in; } std::string StringHelper::RTrimAll(const std::string &in, const std::string &suffix) { if (suffix.empty()) { return in; } std::string copy(in); while (EndsWith(copy, suffix)) { copy = copy.substr(0, copy.size() - suffix.size()); } return copy; } std::string StringHelper::LTrimAll(const std::string &in, const std::string &prefix) { if (prefix.empty()) { return in; } std::string copy(in); while (StartsWith(copy, prefix)) { copy = copy.substr(prefix.size()); } return copy; } void StringHelper::SplitString( const std::string &s, char c, std::vector *components) { components->clear(); size_t startPos = 0; size_t matchPos; while ((matchPos = s.find(c, startPos)) != std::string::npos) { components->push_back(s.substr(startPos, matchPos - startPos)); startPos = matchPos + 1; } if (startPos <= s.length()) { components->push_back(s.substr(startPos)); } } std::string StringHelper::JoinStrings( const std::vector &components, const std::string &separator) { std::string out; bool first = true; for (const auto &component : components) { if (!first) { out += separator; } out += component; first = false; } return out; } } // namespace android