blob: 74ad46ef13266e2944dd8d7d67d0e3ab25338069 [file] [log] [blame]
/*
* 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