1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "StringHelper.h" 18 19 #include <cctype> 20 #include <regex> 21 #include <sstream> 22 #include <string> 23 #include <vector> 24 25 #include <android-base/logging.h> 26 #include <android-base/macros.h> 27 28 #define UPPERCASE "[A-Z0-9]" 29 #define LOWERCASE "[a-z][a-z0-9]*" 30 #define NUMCASE "[0-9]" 31 #define CAPCASE "[A-Z][a-z][a-z0-9]*" 32 static const std::regex kStartUppercase("^" UPPERCASE); 33 static const std::regex kStartLowercase("^" LOWERCASE); 34 static const std::regex kStartCapcase("^" CAPCASE); 35 static const std::regex kStartNumcase("^" CAPCASE); 36 37 namespace android { 38 39 std::string StringHelper::Uppercase(const std::string &in) { 40 std::string out{in}; 41 42 for (auto &ch : out) { 43 ch = toupper(ch); 44 } 45 46 return out; 47 } 48 49 std::string StringHelper::Lowercase(const std::string &in) { 50 std::string out{in}; 51 52 for (auto &ch : out) { 53 ch = tolower(ch); 54 } 55 56 return out; 57 } 58 59 std::string StringHelper::Capitalize(const std::string &in) { 60 std::string out{in}; 61 62 if(!out.empty()) { 63 out[0] = toupper(out[0]); 64 } 65 66 return out; 67 } 68 69 // Combines multiple single character upper case tokens together 70 // {"U", "I", "Error"} becomes {"UI", "Error"} 71 static void combineSingleCharTokens(const std::vector<std::string>& from, 72 std::vector<std::string>* to) { 73 std::string current; 74 for (const std::string& str : from) { 75 if (str.size() == 1 && (isupper(str[0]) || isdigit(str[0]))) { 76 current += str; 77 } else { 78 if (!current.empty()) { 79 to->push_back(current); 80 current = ""; 81 } 82 83 to->push_back(str); 84 } 85 } 86 87 if (!current.empty()) to->push_back(current); 88 } 89 90 // Tokenizes strings first based on "_"s and then based on case 91 // PascalCase (CAPCASE) regex is given the highest priority and the remaining uppercase characters 92 // are grouped together. Digits are added to the preceding group, whichever it may be. 93 // Ipv4Addr => {"Ipv4", "Addr"}, V3Bool => {"V3", "Bool"} 94 void StringHelper::Tokenize(const std::string& in, std::vector<std::string>* vec) { 95 vec->clear(); 96 if (in.empty()) return; 97 98 if (in.find('_') != std::string::npos) { 99 std::vector<std::string> snakeCaseComponents; 100 SplitString(in, '_', &snakeCaseComponents); 101 for (const std::string& comp : snakeCaseComponents) { 102 std::vector<std::string> tokens; 103 Tokenize(comp, &tokens); 104 105 vec->insert(vec->end(), tokens.begin(), tokens.end()); 106 } 107 108 return; 109 } 110 111 std::smatch match; 112 std::string copy(in); 113 std::vector<std::string> matches; 114 std::vector<std::string> tmpVec; 115 while (!copy.empty()) { 116 if (std::regex_search(copy, match, kStartLowercase)) matches.push_back(match.str(0)); 117 if (std::regex_search(copy, match, kStartCapcase)) matches.push_back(match.str(0)); 118 if (std::regex_search(copy, match, kStartUppercase)) matches.push_back(match.str(0)); 119 if (std::regex_search(copy, match, kStartNumcase)) matches.push_back(match.str(0)); 120 121 if (!matches.empty()) { 122 std::string& maxmatch = matches[0]; 123 for (std::string& match : matches) 124 if (match.length() > maxmatch.length()) maxmatch = match; 125 tmpVec.push_back(maxmatch); 126 copy = copy.substr(maxmatch.length()); 127 matches.clear(); 128 } else { 129 LOG(WARNING) << "Could not stylize \"" << in << "\""; 130 // don't know what to do, so push back the rest of the string. 131 tmpVec.push_back(copy); 132 } 133 } 134 135 combineSingleCharTokens(tmpVec, vec); 136 } 137 138 std::string StringHelper::ToCamelCase(const std::string &in) { 139 std::vector<std::string> components; 140 Tokenize(in, &components); 141 if (components.empty()) { 142 if (!in.empty()) 143 LOG(WARNING) << "Could not stylize \"" << in << "\""; 144 return in; 145 } 146 components[0] = Lowercase(components[0]); 147 for (size_t i = 1; i < components.size(); i++) { 148 components[i] = Capitalize(components[i]); 149 } 150 return JoinStrings(components, ""); 151 } 152 153 std::string StringHelper::ToPascalCase(const std::string &in) { 154 std::vector<std::string> components; 155 Tokenize(in, &components); 156 for (size_t i = 0; i < components.size(); i++) { 157 components[i] = Capitalize(components[i]); 158 } 159 return JoinStrings(components, ""); 160 } 161 162 std::string StringHelper::ToUpperSnakeCase(const std::string &in) { 163 std::vector<std::string> components; 164 Tokenize(in, &components); 165 for (size_t i = 0; i < components.size(); i++) { 166 components[i] = Uppercase(components[i]); 167 } 168 return JoinStrings(components, "_"); 169 } 170 171 std::string StringHelper::ToLowerSnakeCase(const std::string &in) { 172 std::vector<std::string> components; 173 Tokenize(in, &components); 174 for (size_t i = 0; i < components.size(); i++) { 175 components[i] = Lowercase(components[i]); 176 } 177 return JoinStrings(components, "_"); 178 } 179 180 std::string StringHelper::ToCase(StringHelper::Case c, const std::string &in) { 181 switch(c) { 182 case kCamelCase: 183 return ToCamelCase(in); 184 case kPascalCase: 185 return ToPascalCase(in); 186 case kUpperSnakeCase: 187 return ToUpperSnakeCase(in); 188 case kLowerSnakeCase: 189 return ToLowerSnakeCase(in); 190 case kNoCase: 191 return in; 192 } 193 LOG(FATAL) << "Should not reach here."; 194 return in; 195 } 196 197 bool StringHelper::EndsWith(const std::string &in, const std::string &suffix) { 198 return in.size() >= suffix.size() && 199 in.substr(in.size() - suffix.size()) == suffix; 200 } 201 202 bool StringHelper::StartsWith(const std::string &in, const std::string &prefix) { 203 return in.size() >= prefix.size() && 204 in.substr(0, prefix.size()) == prefix; 205 } 206 207 std::string StringHelper::RTrim(const std::string &in, const std::string &suffix) { 208 if (EndsWith(in, suffix)) { 209 return in.substr(0, in.size() - suffix.size()); 210 } 211 212 return in; 213 } 214 215 std::string StringHelper::LTrim(const std::string &in, const std::string &prefix) { 216 if (StartsWith(in, prefix)) { 217 return in.substr(prefix.size()); 218 } 219 220 return in; 221 } 222 223 std::string StringHelper::RTrimAll(const std::string &in, const std::string &suffix) { 224 if (suffix.empty()) { 225 return in; 226 } 227 228 std::string copy(in); 229 while (EndsWith(copy, suffix)) { 230 copy = copy.substr(0, copy.size() - suffix.size()); 231 } 232 233 return copy; 234 } 235 236 std::string StringHelper::LTrimAll(const std::string &in, const std::string &prefix) { 237 if (prefix.empty()) { 238 return in; 239 } 240 241 std::string copy(in); 242 while (StartsWith(copy, prefix)) { 243 copy = copy.substr(prefix.size()); 244 } 245 246 return copy; 247 } 248 249 void StringHelper::SplitString( 250 const std::string &s, char c, std::vector<std::string> *components) { 251 components->clear(); 252 253 size_t startPos = 0; 254 size_t matchPos; 255 while ((matchPos = s.find(c, startPos)) != std::string::npos) { 256 components->push_back(s.substr(startPos, matchPos - startPos)); 257 startPos = matchPos + 1; 258 } 259 260 if (startPos <= s.length()) { 261 components->push_back(s.substr(startPos)); 262 } 263 } 264 265 std::string StringHelper::JoinStrings( 266 const std::vector<std::string> &components, 267 const std::string &separator) { 268 std::string out; 269 bool first = true; 270 for (const auto &component : components) { 271 if (!first) { 272 out += separator; 273 } 274 out += component; 275 276 first = false; 277 } 278 279 return out; 280 } 281 282 } // namespace android 283 284