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 "FQName.h"
18 
19 #include <android-base/logging.h>
20 #include <android-base/parseint.h>
21 #include <android-base/strings.h>
22 #include <iostream>
23 #include <sstream>
24 
25 namespace android {
26 
FQName()27 FQName::FQName() : mIsIdentifier(false) {}
28 
parse(const std::string & s,FQName * into)29 bool FQName::parse(const std::string& s, FQName* into) {
30     return into->setTo(s);
31 }
32 
FQName(const std::string & package,const std::string & version,const std::string & name,const std::string & valueName)33 FQName::FQName(const std::string& package, const std::string& version, const std::string& name,
34                const std::string& valueName) {
35     size_t majorVer, minorVer;
36     CHECK(parseVersion(version, &majorVer, &minorVer));
37     CHECK(setTo(package, majorVer, minorVer, name, valueName)) << string();
38 }
39 
setTo(const std::string & package,size_t majorVer,size_t minorVer,const std::string & name,const std::string & valueName)40 bool FQName::setTo(const std::string& package, size_t majorVer, size_t minorVer,
41                    const std::string& name, const std::string& valueName) {
42     mPackage = package;
43     mMajor = majorVer;
44     mMinor = minorVer;
45     mName = name;
46     mValueName = valueName;
47 
48     FQName other;
49     if (!parse(string(), &other)) return false;
50     if ((*this) != other) return false;
51     mIsIdentifier = other.isIdentifier();
52     return true;
53 }
54 
isIdentifier() const55 bool FQName::isIdentifier() const {
56     return mIsIdentifier;
57 }
58 
isFullyQualified() const59 bool FQName::isFullyQualified() const {
60     return !mPackage.empty() && !version().empty() && !mName.empty();
61 }
62 
isValidValueName() const63 bool FQName::isValidValueName() const {
64     return mIsIdentifier
65         || (!mName.empty() && !mValueName.empty());
66 }
67 
isInterfaceName() const68 bool FQName::isInterfaceName() const {
69     return !mName.empty() && mName[0] == 'I' && mName.find('.') == std::string::npos;
70 }
71 
isIdentStart(char a)72 static inline bool isIdentStart(char a) {
73     return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z') || a == '_';
74 }
isLeadingDigit(char a)75 static inline bool isLeadingDigit(char a) {
76     return '1' <= a && a <= '9';
77 }
isDigit(char a)78 static inline bool isDigit(char a) {
79     return '0' <= a && a <= '9';
80 }
isIdentBody(char a)81 static inline bool isIdentBody(char a) {
82     return isIdentStart(a) || isDigit(a);
83 }
84 
85 // returns pointer to end of [a-zA-Z_][a-zA-Z0-9_]*
eatIdent(const char * l,const char * end)86 static const char* eatIdent(const char* l, const char* end) {
87     if (!(l < end && isIdentStart(*l++))) return nullptr;
88     while (l < end && isIdentBody(*l)) l++;
89     return l;
90 }
91 
92 // returns pointer to end of <ident>(\.<ident>)*
eatPackage(const char * l,const char * end)93 static const char* eatPackage(const char* l, const char* end) {
94     if ((l = eatIdent(l, end)) == nullptr) return nullptr;
95 
96     while (l < end && *l == '.') {
97         l++;
98         if ((l = eatIdent(l, end)) == nullptr) return nullptr;
99     }
100     return l;
101 }
102 
103 // returns pointer to end of [1-9][0-9]*|0
eatNumber(const char * l,const char * end)104 static const char* eatNumber(const char* l, const char* end) {
105     if (!(l < end)) return nullptr;
106     if (*l == '0') return l + 1;
107     if (!isLeadingDigit(*l++)) return nullptr;
108     while (l < end && isDigit(*l)) l++;
109     return l;
110 }
111 
setTo(const std::string & s)112 bool FQName::setTo(const std::string &s) {
113     clear();
114 
115     if (s.empty()) return false;
116 
117     const char* l = s.c_str();
118     const char* end = l + s.size();
119     // android.hardware.foo@10.12::IFoo.Type:MY_ENUM_VALUE
120     // S                   ES ES E S        ES            E
121     //
122     // S - start pointer
123     // E - end pointer
124 
125     struct StartEnd {
126         const char* start = nullptr;
127         const char* end = nullptr;
128 
129         std::string string() {
130             if (start == nullptr) return std::string();
131             return std::string(start, end - start);
132         }
133     };
134     StartEnd package, major, minor, name, type;
135 
136     if (l < end && isIdentStart(*l)) {
137         package.start = l;
138         if ((package.end = l = eatPackage(l, end)) == nullptr) return false;
139     }
140     if (l < end && *l == '@') {
141         l++;
142 
143         major.start = l;
144         if ((major.end = l = eatNumber(l, end)) == nullptr) return false;
145 
146         if (!(l < end && *l++ == '.')) return false;
147 
148         minor.start = l;
149         if ((minor.end = l = eatNumber(l, end)) == nullptr) return false;
150     }
151     if (l < end && *l == ':') {
152         l++;
153         if (l < end && *l == ':') {
154             l++;
155             name.start = l;
156             if ((name.end = l = eatPackage(l, end)) == nullptr) return false;
157             if (l < end && *l++ == ':') {
158                 type.start = l;
159                 if ((type.end = l = eatIdent(l, end)) == nullptr) return false;
160             }
161         } else {
162             type.start = l;
163             if ((type.end = l = eatIdent(l, end)) == nullptr) return false;
164         }
165     }
166 
167     if (l < end) return false;
168 
169     CHECK((major.start == nullptr) == (minor.start == nullptr));
170 
171     // if we only parse a package, consider this to be a name
172     if (name.start == nullptr && major.start == nullptr) {
173         name.start = package.start;
174         name.end = package.end;
175         package.start = package.end = nullptr;
176     }
177 
178     // failures after this goto fail to clear
179     mName = name.string();
180     mPackage = package.string();
181     mValueName = type.string();
182 
183     if (major.start != nullptr) {
184         if (!parseVersion(major.string(), minor.string(), &mMajor, &mMinor)) goto fail;
185     } else if (mPackage.empty() && mValueName.empty() &&
186                name.end == eatIdent(name.start, name.end)) {
187         // major.start == nullptr
188         mIsIdentifier = true;
189     }
190 
191     if (!mValueName.empty() && mName.empty()) goto fail;
192     if (!mPackage.empty() && version().empty()) goto fail;
193 
194     return true;
195 fail:
196     clear();
197     return false;
198 }
199 
getRelativeFQName(const FQName & relativeTo) const200 std::string FQName::getRelativeFQName(const FQName& relativeTo) const {
201     if (relativeTo.mPackage != mPackage) {
202         return string();
203     }
204 
205     // Package is the same
206     std::string out;
207     if (relativeTo.version() != version()) {
208         out.append(atVersion());
209         if (!mName.empty() && !version().empty()) {
210             out.append("::");
211         }
212     }
213 
214     if (!mName.empty()) {
215         out.append(mName);
216         if (!mValueName.empty()) {
217             out.append(":");
218             out.append(mValueName);
219         }
220     }
221 
222     return out;
223 }
224 
package() const225 const std::string& FQName::package() const {
226     return mPackage;
227 }
228 
version() const229 std::string FQName::version() const {
230     if (!hasVersion()) {
231         return "";
232     }
233     return std::to_string(mMajor) + "." + std::to_string(mMinor);
234 }
235 
sanitizedVersion() const236 std::string FQName::sanitizedVersion() const {
237     if (!hasVersion()) {
238         return "";
239     }
240     return "V" + std::to_string(mMajor) + "_" + std::to_string(mMinor);
241 }
242 
atVersion() const243 std::string FQName::atVersion() const {
244     std::string v = version();
245     return v.empty() ? "" : ("@" + v);
246 }
247 
clear()248 void FQName::clear() {
249     mIsIdentifier = false;
250     mPackage.clear();
251     clearVersion();
252     mName.clear();
253     mValueName.clear();
254 }
255 
clearVersion(size_t * majorVer,size_t * minorVer)256 void FQName::clearVersion(size_t* majorVer, size_t* minorVer) {
257     *majorVer = *minorVer = 0;
258 }
259 
parseVersion(const std::string & majorStr,const std::string & minorStr,size_t * majorVer,size_t * minorVer)260 bool FQName::parseVersion(const std::string& majorStr, const std::string& minorStr,
261                           size_t* majorVer, size_t* minorVer) {
262     bool versionParseSuccess = ::android::base::ParseUint(majorStr, majorVer) &&
263                                ::android::base::ParseUint(minorStr, minorVer);
264     if (!versionParseSuccess) {
265         LOG(ERROR) << "numbers in " << majorStr << "." << minorStr << " are out of range.";
266     }
267     return versionParseSuccess;
268 }
269 
parseVersion(const std::string & v,size_t * majorVer,size_t * minorVer)270 bool FQName::parseVersion(const std::string& v, size_t* majorVer, size_t* minorVer) {
271     if (v.empty()) {
272         clearVersion(majorVer, minorVer);
273         return true;
274     }
275 
276     std::vector<std::string> vs = base::Split(v, ".");
277     if (vs.size() != 2) return false;
278     return parseVersion(vs[0], vs[1], majorVer, minorVer);
279 }
280 
setVersion(const std::string & v)281 bool FQName::setVersion(const std::string& v) {
282     return parseVersion(v, &mMajor, &mMinor);
283 }
284 
clearVersion()285 void FQName::clearVersion() {
286     clearVersion(&mMajor, &mMinor);
287 }
288 
parseVersion(const std::string & majorStr,const std::string & minorStr)289 bool FQName::parseVersion(const std::string& majorStr, const std::string& minorStr) {
290     return parseVersion(majorStr, minorStr, &mMajor, &mMinor);
291 }
292 
name() const293 const std::string& FQName::name() const {
294     return mName;
295 }
296 
names() const297 std::vector<std::string> FQName::names() const {
298     std::vector<std::string> res {};
299     std::istringstream ss(name());
300     std::string s;
301     while (std::getline(ss, s, '.')) {
302         res.push_back(s);
303     }
304     return res;
305 }
306 
valueName() const307 const std::string& FQName::valueName() const {
308     return mValueName;
309 }
310 
typeName() const311 FQName FQName::typeName() const {
312     return FQName(mPackage, version(), mName);
313 }
314 
applyDefaults(const std::string & defaultPackage,const std::string & defaultVersion)315 void FQName::applyDefaults(
316         const std::string &defaultPackage,
317         const std::string &defaultVersion) {
318 
319     // package without version is not allowed.
320     CHECK(mPackage.empty() || !version().empty());
321 
322     if (mPackage.empty()) {
323         mPackage = defaultPackage;
324     }
325 
326     if (version().empty()) {
327         CHECK(setVersion(defaultVersion));
328     }
329 }
330 
string() const331 std::string FQName::string() const {
332     std::string out;
333     out.append(mPackage);
334     out.append(atVersion());
335     if (!mName.empty()) {
336         if (!mPackage.empty() || !version().empty()) {
337             out.append("::");
338         }
339         out.append(mName);
340 
341         if (!mValueName.empty()) {
342             out.append(":");
343             out.append(mValueName);
344         }
345     }
346 
347     return out;
348 }
349 
operator <(const FQName & other) const350 bool FQName::operator<(const FQName &other) const {
351     return string() < other.string();
352 }
353 
operator ==(const FQName & other) const354 bool FQName::operator==(const FQName &other) const {
355     return string() == other.string();
356 }
357 
operator !=(const FQName & other) const358 bool FQName::operator!=(const FQName &other) const {
359     return !(*this == other);
360 }
361 
getInterfaceName() const362 const std::string& FQName::getInterfaceName() const {
363     CHECK(isInterfaceName()) << mName;
364 
365     return mName;
366 }
367 
getInterfaceBaseName() const368 std::string FQName::getInterfaceBaseName() const {
369     // cut off the leading 'I'.
370     return getInterfaceName().substr(1);
371 }
372 
getInterfaceAdapterName() const373 std::string FQName::getInterfaceAdapterName() const {
374     return "A" + getInterfaceBaseName();
375 }
376 
getInterfaceHwName() const377 std::string FQName::getInterfaceHwName() const {
378     return "IHw" + getInterfaceBaseName();
379 }
380 
getInterfaceProxyName() const381 std::string FQName::getInterfaceProxyName() const {
382     return "BpHw" + getInterfaceBaseName();
383 }
384 
getInterfaceStubName() const385 std::string FQName::getInterfaceStubName() const {
386     return "BnHw" + getInterfaceBaseName();
387 }
388 
getInterfacePassthroughName() const389 std::string FQName::getInterfacePassthroughName() const {
390     return "Bs" + getInterfaceBaseName();
391 }
392 
getInterfaceProxyFqName() const393 FQName FQName::getInterfaceProxyFqName() const {
394     return FQName(package(), version(), getInterfaceProxyName());
395 }
396 
getInterfaceAdapterFqName() const397 FQName FQName::getInterfaceAdapterFqName() const {
398     return FQName(package(), version(), getInterfaceAdapterName());
399 }
400 
getInterfaceStubFqName() const401 FQName FQName::getInterfaceStubFqName() const {
402     return FQName(package(), version(), getInterfaceStubName());
403 }
404 
getInterfacePassthroughFqName() const405 FQName FQName::getInterfacePassthroughFqName() const {
406     return FQName(package(), version(), getInterfacePassthroughName());
407 }
408 
getTypesForPackage() const409 FQName FQName::getTypesForPackage() const {
410     return FQName(package(), version(), "types");
411 }
412 
getPackageAndVersion() const413 FQName FQName::getPackageAndVersion() const {
414     return FQName(package(), version(), "");
415 }
416 
getTopLevelType() const417 FQName FQName::getTopLevelType() const {
418     auto idx = mName.find('.');
419 
420     if (idx == std::string::npos) {
421         return *this;
422     }
423 
424     return FQName(mPackage, version(), mName.substr(0, idx));
425 }
426 
tokenName() const427 std::string FQName::tokenName() const {
428     std::vector<std::string> components = getPackageAndVersionComponents(true /* sanitized */);
429 
430     if (!mName.empty()) {
431         std::vector<std::string> nameComponents = base::Split(mName, ".");
432 
433         components.insert(components.end(), nameComponents.begin(), nameComponents.end());
434     }
435 
436     return base::Join(components, "_");
437 }
438 
cppNamespace() const439 std::string FQName::cppNamespace() const {
440     std::vector<std::string> components = getPackageAndVersionComponents(true /* sanitized */);
441     return "::" + base::Join(components, "::");
442 }
443 
cppLocalName() const444 std::string FQName::cppLocalName() const {
445     std::vector<std::string> components = base::Split(mName, ".");
446 
447     return base::Join(components, "::")
448             + (mValueName.empty() ? "" : ("::" + mValueName));
449 }
450 
cppName() const451 std::string FQName::cppName() const {
452     std::string out = cppNamespace();
453 
454     std::vector<std::string> components = base::Split(name(), ".");
455     out += "::";
456     out += base::Join(components, "::");
457     if (!mValueName.empty()) {
458         out  += "::" + mValueName;
459     }
460 
461     return out;
462 }
463 
javaPackage() const464 std::string FQName::javaPackage() const {
465     std::vector<std::string> components = getPackageAndVersionComponents(true /* sanitized */);
466     return base::Join(components, ".");
467 }
468 
javaName() const469 std::string FQName::javaName() const {
470     return javaPackage() + "." + name()
471             + (mValueName.empty() ? "" : ("." + mValueName));
472 }
473 
getPackageComponents() const474 std::vector<std::string> FQName::getPackageComponents() const {
475     return base::Split(package(), ".");
476 }
477 
getPackageAndVersionComponents(bool sanitized) const478 std::vector<std::string> FQName::getPackageAndVersionComponents(bool sanitized) const {
479     CHECK(hasVersion()) << string() << ": getPackageAndVersionComponents expects version.";
480 
481     std::vector<std::string> components = getPackageComponents();
482     if (sanitized) {
483         components.push_back(sanitizedVersion());
484     } else {
485         components.push_back(version());
486     }
487     return components;
488 }
489 
hasVersion() const490 bool FQName::hasVersion() const {
491     return mMajor > 0;
492 }
493 
getVersion() const494 std::pair<size_t, size_t> FQName::getVersion() const {
495     return {mMajor, mMinor};
496 }
497 
withVersion(size_t major,size_t minor) const498 FQName FQName::withVersion(size_t major, size_t minor) const {
499     FQName ret(*this);
500     ret.mMajor = major;
501     ret.mMinor = minor;
502     return ret;
503 }
504 
getPackageMajorVersion() const505 size_t FQName::getPackageMajorVersion() const {
506     CHECK(hasVersion()) << "FQName: No version exists at getPackageMajorVersion(). "
507                         << "Did you check hasVersion()?";
508     return mMajor;
509 }
510 
getPackageMinorVersion() const511 size_t FQName::getPackageMinorVersion() const {
512     CHECK(hasVersion()) << "FQName: No version exists at getPackageMinorVersion(). "
513                         << "Did you check hasVersion()?";
514     return mMinor;
515 }
516 
endsWith(const FQName & other) const517 bool FQName::endsWith(const FQName &other) const {
518     std::string s1 = string();
519     std::string s2 = other.string();
520 
521     size_t pos = s1.rfind(s2);
522     if (pos == std::string::npos || pos + s2.size() != s1.size()) {
523         return false;
524     }
525 
526     // A match is only a match if it is preceded by a "boundary", i.e.
527     // we perform a component-wise match from the end.
528     // "az" is not a match for "android.hardware.foo@1.0::IFoo.bar.baz",
529     // "baz", "bar.baz", "IFoo.bar.baz", "@1.0::IFoo.bar.baz" are.
530     if (pos == 0) {
531         // matches "android.hardware.foo@1.0::IFoo.bar.baz"
532         return true;
533     }
534 
535     if (s1[pos - 1] == '.') {
536         // matches "baz" and "bar.baz"
537         return true;
538     }
539 
540     if (s1[pos - 1] == ':') {
541         // matches "IFoo.bar.baz"
542         return true;
543     }
544 
545     if (s1[pos] == '@') {
546         // matches "@1.0::IFoo.bar.baz"
547         return true;
548     }
549 
550     return false;
551 }
552 
inPackage(const std::string & package) const553 bool FQName::inPackage(const std::string &package) const {
554     std::vector<std::string> components = getPackageComponents();
555     std::vector<std::string> inComponents = base::Split(package, ".");
556 
557     if (inComponents.size() > components.size()) {
558         return false;
559     }
560 
561     for (size_t i = 0; i < inComponents.size(); i++) {
562         if (inComponents[i] != components[i]) {
563             return false;
564         }
565     }
566 
567     return true;
568 }
569 
downRev() const570 FQName FQName::downRev() const {
571     FQName ret(*this);
572     CHECK(ret.mMinor > 0);
573     ret.mMinor--;
574     return ret;
575 }
576 
upRev() const577 FQName FQName::upRev() const {
578     FQName ret(*this);
579     ret.mMinor++;
580     CHECK(ret.mMinor > 0);
581     return ret;
582 }
583 
584 }  // namespace android
585 
586