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