/*
* 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 <sstream>
#include <regex>
#include <android-base/macros.h>
#include <android-base/logging.h>
#define UPPERCASE "[A-Z0-9]+"
#define LOWERCASE "[a-z0-9]+"
#define CAPCASE "[A-Z0-9][a-z0-9]*"
static const std::regex kStartUppercase("^" UPPERCASE);
static const std::regex kStartLowercase("^" LOWERCASE);
static const std::regex kStartCapcase("^" CAPCASE);
namespace android {
// static
std::string StringHelper::Uppercase(const std::string &in) {
std::string out{in};
for (auto &ch : out) {
ch = toupper(ch);
}
return out;
}
// static
std::string StringHelper::Lowercase(const std::string &in) {
std::string out{in};
for (auto &ch : out) {
ch = tolower(ch);
}
return out;
}
// static
std::string StringHelper::Capitalize(const std::string &in) {
std::string out{in};
if(!out.empty()) {
out[0] = toupper(out[0]);
}
return out;
}
// static
void StringHelper::Tokenize(const std::string &in,
std::vector<std::string> *vec) {
std::smatch match;
if (in.empty()) {
vec->clear();
return;
}
std::string copy(in);
vec->clear();
std::vector<std::string> matches;
copy = RTrimAll(copy, "_");
while(!copy.empty()) {
copy = LTrimAll(copy, "_");
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 (!matches.empty()) {
std::string &maxmatch = matches[0];
for (std::string &match : matches)
if(match.length() > maxmatch.length())
maxmatch = match;
vec->push_back(maxmatch);
copy = copy.substr(maxmatch.length());
matches.clear();
continue;
}
LOG(WARNING) << "Could not stylize \"" << in << "\"";
// don't know what to do, so push back the rest of the string.
vec->push_back(copy);
}
}
// static
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, "");
}
// static
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, "");
}
// static
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, "_");
}
// static
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, "_");
}
// static
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;
}
// static
bool StringHelper::EndsWith(const std::string &in, const std::string &suffix) {
return in.size() >= suffix.size() &&
in.substr(in.size() - suffix.size()) == suffix;
}
// static
bool StringHelper::StartsWith(const std::string &in, const std::string &prefix) {
return in.size() >= prefix.size() &&
in.substr(0, prefix.size()) == prefix;
}
// static
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;
}
// static
std::string StringHelper::LTrim(const std::string &in, const std::string &prefix) {
if (StartsWith(in, prefix)) {
return in.substr(prefix.size());
}
return in;
}
// static
std::string StringHelper::RTrimAll(const std::string &in, const std::string &suffix) {
std::string copy(in);
while (EndsWith(copy, suffix)) {
copy = copy.substr(0, copy.size() - suffix.size());
}
return copy;
}
// static
std::string StringHelper::LTrimAll(const std::string &in, const std::string &prefix) {
std::string copy(in);
while (StartsWith(copy, prefix)) {
copy = copy.substr(prefix.size());
}
return copy;
}
// static
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));
}
}
// static
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