| /* |
| * 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 <cctype> |
| #include <regex> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include <android-base/logging.h> |
| #include <android-base/macros.h> |
| |
| #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<std::string>& from, |
| std::vector<std::string>* 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<std::string>* vec) { |
| vec->clear(); |
| if (in.empty()) return; |
| |
| if (in.find('_') != std::string::npos) { |
| std::vector<std::string> snakeCaseComponents; |
| SplitString(in, '_', &snakeCaseComponents); |
| for (const std::string& comp : snakeCaseComponents) { |
| std::vector<std::string> tokens; |
| Tokenize(comp, &tokens); |
| |
| vec->insert(vec->end(), tokens.begin(), tokens.end()); |
| } |
| |
| return; |
| } |
| |
| std::smatch match; |
| std::string copy(in); |
| std::vector<std::string> matches; |
| std::vector<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> *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<std::string> &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 |
| |