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 <sstream>
20 #include <regex>
21
22 #include <android-base/macros.h>
23 #include <android-base/logging.h>
24
25 #define UPPERCASE "[A-Z0-9]+"
26 #define LOWERCASE "[a-z0-9]+"
27 #define CAPCASE "[A-Z0-9][a-z0-9]*"
28 static const std::regex kStartUppercase("^" UPPERCASE);
29 static const std::regex kStartLowercase("^" LOWERCASE);
30 static const std::regex kStartCapcase("^" CAPCASE);
31
32 namespace android {
33
34 // static
Uppercase(const std::string & in)35 std::string StringHelper::Uppercase(const std::string &in) {
36 std::string out{in};
37
38 for (auto &ch : out) {
39 ch = toupper(ch);
40 }
41
42 return out;
43 }
44
45 // static
Lowercase(const std::string & in)46 std::string StringHelper::Lowercase(const std::string &in) {
47 std::string out{in};
48
49 for (auto &ch : out) {
50 ch = tolower(ch);
51 }
52
53 return out;
54 }
55
56 // static
Capitalize(const std::string & in)57 std::string StringHelper::Capitalize(const std::string &in) {
58 std::string out{in};
59
60 if(!out.empty()) {
61 out[0] = toupper(out[0]);
62 }
63
64 return out;
65 }
66
67 // static
Tokenize(const std::string & in,std::vector<std::string> * vec)68 void StringHelper::Tokenize(const std::string &in,
69 std::vector<std::string> *vec) {
70
71 std::smatch match;
72 if (in.empty()) {
73 vec->clear();
74 return;
75 }
76 std::string copy(in);
77 vec->clear();
78 std::vector<std::string> matches;
79
80 copy = RTrimAll(copy, "_");
81 while(!copy.empty()) {
82 copy = LTrimAll(copy, "_");
83 if (std::regex_search(copy, match, kStartLowercase))
84 matches.push_back(match.str(0));
85 if (std::regex_search(copy, match, kStartCapcase))
86 matches.push_back(match.str(0));
87 if (std::regex_search(copy, match, kStartUppercase))
88 matches.push_back(match.str(0));
89 if (!matches.empty()) {
90 std::string &maxmatch = matches[0];
91 for (std::string &match : matches)
92 if(match.length() > maxmatch.length())
93 maxmatch = match;
94 vec->push_back(maxmatch);
95 copy = copy.substr(maxmatch.length());
96 matches.clear();
97 continue;
98 }
99 LOG(WARNING) << "Could not stylize \"" << in << "\"";
100 // don't know what to do, so push back the rest of the string.
101 vec->push_back(copy);
102 }
103 }
104
105 // static
ToCamelCase(const std::string & in)106 std::string StringHelper::ToCamelCase(const std::string &in) {
107 std::vector<std::string> components;
108 Tokenize(in, &components);
109 if (components.empty()) {
110 if (!in.empty())
111 LOG(WARNING) << "Could not stylize \"" << in << "\"";
112 return in;
113 }
114 components[0] = Lowercase(components[0]);
115 for (size_t i = 1; i < components.size(); i++) {
116 components[i] = Capitalize(components[i]);
117 }
118 return JoinStrings(components, "");
119 }
120
121 // static
ToPascalCase(const std::string & in)122 std::string StringHelper::ToPascalCase(const std::string &in) {
123 std::vector<std::string> components;
124 Tokenize(in, &components);
125 for (size_t i = 0; i < components.size(); i++) {
126 components[i] = Capitalize(components[i]);
127 }
128 return JoinStrings(components, "");
129 }
130
131 // static
ToUpperSnakeCase(const std::string & in)132 std::string StringHelper::ToUpperSnakeCase(const std::string &in) {
133 std::vector<std::string> components;
134 Tokenize(in, &components);
135 for (size_t i = 0; i < components.size(); i++) {
136 components[i] = Uppercase(components[i]);
137 }
138 return JoinStrings(components, "_");
139 }
140
141 // static
ToLowerSnakeCase(const std::string & in)142 std::string StringHelper::ToLowerSnakeCase(const std::string &in) {
143 std::vector<std::string> components;
144 Tokenize(in, &components);
145 for (size_t i = 0; i < components.size(); i++) {
146 components[i] = Lowercase(components[i]);
147 }
148 return JoinStrings(components, "_");
149 }
150
151 // static
ToCase(StringHelper::Case c,const std::string & in)152 std::string StringHelper::ToCase(StringHelper::Case c, const std::string &in) {
153 switch(c) {
154 case kCamelCase:
155 return ToCamelCase(in);
156 case kPascalCase:
157 return ToPascalCase(in);
158 case kUpperSnakeCase:
159 return ToUpperSnakeCase(in);
160 case kLowerSnakeCase:
161 return ToLowerSnakeCase(in);
162 case kNoCase:
163 return in;
164 }
165 LOG(FATAL) << "Should not reach here.";
166 return in;
167 }
168
169 // static
EndsWith(const std::string & in,const std::string & suffix)170 bool StringHelper::EndsWith(const std::string &in, const std::string &suffix) {
171 return in.size() >= suffix.size() &&
172 in.substr(in.size() - suffix.size()) == suffix;
173 }
174
175 // static
StartsWith(const std::string & in,const std::string & prefix)176 bool StringHelper::StartsWith(const std::string &in, const std::string &prefix) {
177 return in.size() >= prefix.size() &&
178 in.substr(0, prefix.size()) == prefix;
179 }
180
181 // static
RTrim(const std::string & in,const std::string & suffix)182 std::string StringHelper::RTrim(const std::string &in, const std::string &suffix) {
183 if (EndsWith(in, suffix)) {
184 return in.substr(0, in.size() - suffix.size());
185 }
186
187 return in;
188 }
189
190 // static
LTrim(const std::string & in,const std::string & prefix)191 std::string StringHelper::LTrim(const std::string &in, const std::string &prefix) {
192 if (StartsWith(in, prefix)) {
193 return in.substr(prefix.size());
194 }
195
196 return in;
197 }
198
199 // static
RTrimAll(const std::string & in,const std::string & suffix)200 std::string StringHelper::RTrimAll(const std::string &in, const std::string &suffix) {
201 std::string copy(in);
202 while (EndsWith(copy, suffix)) {
203 copy = copy.substr(0, copy.size() - suffix.size());
204 }
205
206 return copy;
207 }
208
209 // static
LTrimAll(const std::string & in,const std::string & prefix)210 std::string StringHelper::LTrimAll(const std::string &in, const std::string &prefix) {
211 std::string copy(in);
212 while (StartsWith(copy, prefix)) {
213 copy = copy.substr(prefix.size());
214 }
215
216 return copy;
217 }
218
219 // static
SplitString(const std::string & s,char c,std::vector<std::string> * components)220 void StringHelper::SplitString(
221 const std::string &s, char c, std::vector<std::string> *components) {
222 components->clear();
223
224 size_t startPos = 0;
225 size_t matchPos;
226 while ((matchPos = s.find(c, startPos)) != std::string::npos) {
227 components->push_back(s.substr(startPos, matchPos - startPos));
228 startPos = matchPos + 1;
229 }
230
231 if (startPos <= s.length()) {
232 components->push_back(s.substr(startPos));
233 }
234 }
235
236 // static
JoinStrings(const std::vector<std::string> & components,const std::string & separator)237 std::string StringHelper::JoinStrings(
238 const std::vector<std::string> &components,
239 const std::string &separator) {
240 std::string out;
241 bool first = true;
242 for (const auto &component : components) {
243 if (!first) {
244 out += separator;
245 }
246 out += component;
247
248 first = false;
249 }
250
251 return out;
252 }
253
254 } // namespace android
255
256