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