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 
FQName(const FQName & other)55 FQName::FQName(const FQName& other)
56     : mIsIdentifier(other.mIsIdentifier),
57       mPackage(other.mPackage),
58       mMajor(other.mMajor),
59       mMinor(other.mMinor),
60       mName(other.mName),
61       mValueName(other.mValueName) {}
62 
isIdentifier() const63 bool FQName::isIdentifier() const {
64     return mIsIdentifier;
65 }
66 
isFullyQualified() const67 bool FQName::isFullyQualified() const {
68     return !mPackage.empty() && !version().empty() && !mName.empty();
69 }
70 
isValidValueName() const71 bool FQName::isValidValueName() const {
72     return mIsIdentifier
73         || (!mName.empty() && !mValueName.empty());
74 }
75 
isInterfaceName() const76 bool FQName::isInterfaceName() const {
77     return !mName.empty() && mName[0] == 'I' && mName.find('.') == std::string::npos;
78 }
79 
isIdentStart(char a)80 static inline bool isIdentStart(char a) {
81     return ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z') || a == '_';
82 }
isLeadingDigit(char a)83 static inline bool isLeadingDigit(char a) {
84     return '1' <= a && a <= '9';
85 }
isDigit(char a)86 static inline bool isDigit(char a) {
87     return '0' <= a && a <= '9';
88 }
isIdentBody(char a)89 static inline bool isIdentBody(char a) {
90     return isIdentStart(a) || isDigit(a);
91 }
92 
93 // returns pointer to end of [a-zA-Z_][a-zA-Z0-9_]*
eatIdent(const char * l,const char * end)94 static const char* eatIdent(const char* l, const char* end) {
95     if (!(l < end && isIdentStart(*l++))) return nullptr;
96     while (l < end && isIdentBody(*l)) l++;
97     return l;
98 }
99 
100 // returns pointer to end of <ident>(\.<ident>)*
eatPackage(const char * l,const char * end)101 static const char* eatPackage(const char* l, const char* end) {
102     if ((l = eatIdent(l, end)) == nullptr) return nullptr;
103 
104     while (l < end && *l == '.') {
105         l++;
106         if ((l = eatIdent(l, end)) == nullptr) return nullptr;
107     }
108     return l;
109 }
110 
111 // returns pointer to end of [1-9][0-9]*|0
eatNumber(const char * l,const char * end)112 static const char* eatNumber(const char* l, const char* end) {
113     if (!(l < end)) return nullptr;
114     if (*l == '0') return l + 1;
115     if (!isLeadingDigit(*l++)) return nullptr;
116     while (l < end && isDigit(*l)) l++;
117     return l;
118 }
119 
setTo(const std::string & s)120 bool FQName::setTo(const std::string &s) {
121     clear();
122 
123     if (s.empty()) return false;
124 
125     const char* l = s.c_str();
126     const char* end = l + s.size();
127     // android.hardware.foo@10.12::IFoo.Type:MY_ENUM_VALUE
128     // S                   ES ES E S        ES            E
129     //
130     // S - start pointer
131     // E - end pointer
132 
133     struct StartEnd {
134         const char* start = nullptr;
135         const char* end = nullptr;
136 
137         std::string string() {
138             if (start == nullptr) return std::string();
139             return std::string(start, end - start);
140         }
141     };
142     StartEnd package, major, minor, name, type;
143 
144     if (l < end && isIdentStart(*l)) {
145         package.start = l;
146         if ((package.end = l = eatPackage(l, end)) == nullptr) return false;
147     }
148     if (l < end && *l == '@') {
149         l++;
150 
151         major.start = l;
152         if ((major.end = l = eatNumber(l, end)) == nullptr) return false;
153 
154         if (!(l < end && *l++ == '.')) return false;
155 
156         minor.start = l;
157         if ((minor.end = l = eatNumber(l, end)) == nullptr) return false;
158     }
159     if (l < end && *l == ':') {
160         l++;
161         if (l < end && *l == ':') {
162             l++;
163             name.start = l;
164             if ((name.end = l = eatPackage(l, end)) == nullptr) return false;
165             if (l < end && *l++ == ':') {
166                 type.start = l;
167                 if ((type.end = l = eatIdent(l, end)) == nullptr) return false;
168             }
169         } else {
170             type.start = l;
171             if ((type.end = l = eatIdent(l, end)) == nullptr) return false;
172         }
173     }
174 
175     if (l < end) return false;
176 
177     CHECK((major.start == nullptr) == (minor.start == nullptr));
178 
179     // if we only parse a package, consider this to be a name
180     if (name.start == nullptr && major.start == nullptr) {
181         name.start = package.start;
182         name.end = package.end;
183         package.start = package.end = nullptr;
184     }
185 
186     // failures after this goto fail to clear
187     mName = name.string();
188     mPackage = package.string();
189     mValueName = type.string();
190 
191     if (major.start != nullptr) {
192         if (!parseVersion(major.string(), minor.string(), &mMajor, &mMinor)) goto fail;
193     } else if (mPackage.empty() && mValueName.empty() &&
194                name.end == eatIdent(name.start, name.end)) {
195         // major.start == nullptr
196         mIsIdentifier = true;
197     }
198 
199     if (!mValueName.empty() && mName.empty()) goto fail;
200     if (!mPackage.empty() && version().empty()) goto fail;
201 
202     return true;
203 fail:
204     clear();
205     return false;
206 }
207 
getRelativeFQName(const FQName & relativeTo) const208 std::string FQName::getRelativeFQName(const FQName& relativeTo) const {
209     if (relativeTo.mPackage != mPackage) {
210         return string();
211     }
212 
213     // Package is the same
214     std::string out;
215     if (relativeTo.version() != version()) {
216         out.append(atVersion());
217         if (!mName.empty() && !version().empty()) {
218             out.append("::");
219         }
220     }
221 
222     if (!mName.empty()) {
223         out.append(mName);
224         if (!mValueName.empty()) {
225             out.append(":");
226             out.append(mValueName);
227         }
228     }
229 
230     return out;
231 }
232 
package() const233 const std::string& FQName::package() const {
234     return mPackage;
235 }
236 
version() const237 std::string FQName::version() const {
238     if (!hasVersion()) {
239         return "";
240     }
241     return std::to_string(mMajor) + "." + std::to_string(mMinor);
242 }
243 
sanitizedVersion() const244 std::string FQName::sanitizedVersion() const {
245     if (!hasVersion()) {
246         return "";
247     }
248     return "V" + std::to_string(mMajor) + "_" + std::to_string(mMinor);
249 }
250 
atVersion() const251 std::string FQName::atVersion() const {
252     std::string v = version();
253     return v.empty() ? "" : ("@" + v);
254 }
255 
clear()256 void FQName::clear() {
257     mIsIdentifier = false;
258     mPackage.clear();
259     clearVersion();
260     mName.clear();
261     mValueName.clear();
262 }
263 
clearVersion(size_t * majorVer,size_t * minorVer)264 void FQName::clearVersion(size_t* majorVer, size_t* minorVer) {
265     *majorVer = *minorVer = 0;
266 }
267 
parseVersion(const std::string & majorStr,const std::string & minorStr,size_t * majorVer,size_t * minorVer)268 bool FQName::parseVersion(const std::string& majorStr, const std::string& minorStr,
269                           size_t* majorVer, size_t* minorVer) {
270     bool versionParseSuccess = ::android::base::ParseUint(majorStr, majorVer) &&
271                                ::android::base::ParseUint(minorStr, minorVer);
272     if (!versionParseSuccess) {
273         LOG(ERROR) << "numbers in " << majorStr << "." << minorStr << " are out of range.";
274     }
275     return versionParseSuccess;
276 }
277 
parseVersion(const std::string & v,size_t * majorVer,size_t * minorVer)278 bool FQName::parseVersion(const std::string& v, size_t* majorVer, size_t* minorVer) {
279     if (v.empty()) {
280         clearVersion(majorVer, minorVer);
281         return true;
282     }
283 
284     std::vector<std::string> vs = base::Split(v, ".");
285     if (vs.size() != 2) return false;
286     return parseVersion(vs[0], vs[1], majorVer, minorVer);
287 }
288 
setVersion(const std::string & v)289 bool FQName::setVersion(const std::string& v) {
290     return parseVersion(v, &mMajor, &mMinor);
291 }
292 
clearVersion()293 void FQName::clearVersion() {
294     clearVersion(&mMajor, &mMinor);
295 }
296 
parseVersion(const std::string & majorStr,const std::string & minorStr)297 bool FQName::parseVersion(const std::string& majorStr, const std::string& minorStr) {
298     return parseVersion(majorStr, minorStr, &mMajor, &mMinor);
299 }
300 
name() const301 const std::string& FQName::name() const {
302     return mName;
303 }
304 
names() const305 std::vector<std::string> FQName::names() const {
306     std::vector<std::string> res {};
307     std::istringstream ss(name());
308     std::string s;
309     while (std::getline(ss, s, '.')) {
310         res.push_back(s);
311     }
312     return res;
313 }
314 
valueName() const315 const std::string& FQName::valueName() const {
316     return mValueName;
317 }
318 
typeName() const319 FQName FQName::typeName() const {
320     return FQName(mPackage, version(), mName);
321 }
322 
applyDefaults(const std::string & defaultPackage,const std::string & defaultVersion)323 void FQName::applyDefaults(
324         const std::string &defaultPackage,
325         const std::string &defaultVersion) {
326 
327     // package without version is not allowed.
328     CHECK(mPackage.empty() || !version().empty());
329 
330     if (mPackage.empty()) {
331         mPackage = defaultPackage;
332     }
333 
334     if (version().empty()) {
335         CHECK(setVersion(defaultVersion));
336     }
337 }
338 
string() const339 std::string FQName::string() const {
340     std::string out;
341     out.append(mPackage);
342     out.append(atVersion());
343     if (!mName.empty()) {
344         if (!mPackage.empty() || !version().empty()) {
345             out.append("::");
346         }
347         out.append(mName);
348 
349         if (!mValueName.empty()) {
350             out.append(":");
351             out.append(mValueName);
352         }
353     }
354 
355     return out;
356 }
357 
operator <(const FQName & other) const358 bool FQName::operator<(const FQName &other) const {
359     return string() < other.string();
360 }
361 
operator ==(const FQName & other) const362 bool FQName::operator==(const FQName &other) const {
363     return string() == other.string();
364 }
365 
operator !=(const FQName & other) const366 bool FQName::operator!=(const FQName &other) const {
367     return !(*this == other);
368 }
369 
getInterfaceName() const370 const std::string& FQName::getInterfaceName() const {
371     CHECK(isInterfaceName()) << mName;
372 
373     return mName;
374 }
375 
getInterfaceBaseName() const376 std::string FQName::getInterfaceBaseName() const {
377     // cut off the leading 'I'.
378     return getInterfaceName().substr(1);
379 }
380 
getInterfaceAdapterName() const381 std::string FQName::getInterfaceAdapterName() const {
382     return "A" + getInterfaceBaseName();
383 }
384 
getInterfaceHwName() const385 std::string FQName::getInterfaceHwName() const {
386     return "IHw" + getInterfaceBaseName();
387 }
388 
getInterfaceProxyName() const389 std::string FQName::getInterfaceProxyName() const {
390     return "BpHw" + getInterfaceBaseName();
391 }
392 
getInterfaceStubName() const393 std::string FQName::getInterfaceStubName() const {
394     return "BnHw" + getInterfaceBaseName();
395 }
396 
getInterfacePassthroughName() const397 std::string FQName::getInterfacePassthroughName() const {
398     return "Bs" + getInterfaceBaseName();
399 }
400 
getInterfaceProxyFqName() const401 FQName FQName::getInterfaceProxyFqName() const {
402     return FQName(package(), version(), getInterfaceProxyName());
403 }
404 
getInterfaceAdapterFqName() const405 FQName FQName::getInterfaceAdapterFqName() const {
406     return FQName(package(), version(), getInterfaceAdapterName());
407 }
408 
getInterfaceStubFqName() const409 FQName FQName::getInterfaceStubFqName() const {
410     return FQName(package(), version(), getInterfaceStubName());
411 }
412 
getInterfacePassthroughFqName() const413 FQName FQName::getInterfacePassthroughFqName() const {
414     return FQName(package(), version(), getInterfacePassthroughName());
415 }
416 
getTypesForPackage() const417 FQName FQName::getTypesForPackage() const {
418     return FQName(package(), version(), "types");
419 }
420 
getPackageAndVersion() const421 FQName FQName::getPackageAndVersion() const {
422     return FQName(package(), version(), "");
423 }
424 
getTopLevelType() const425 FQName FQName::getTopLevelType() const {
426     auto idx = mName.find('.');
427 
428     if (idx == std::string::npos) {
429         return *this;
430     }
431 
432     return FQName(mPackage, version(), mName.substr(0, idx));
433 }
434 
tokenName() const435 std::string FQName::tokenName() const {
436     std::vector<std::string> components = getPackageAndVersionComponents(true /* sanitized */);
437 
438     if (!mName.empty()) {
439         std::vector<std::string> nameComponents = base::Split(mName, ".");
440 
441         components.insert(components.end(), nameComponents.begin(), nameComponents.end());
442     }
443 
444     return base::Join(components, "_");
445 }
446 
cppNamespace() const447 std::string FQName::cppNamespace() const {
448     std::vector<std::string> components = getPackageAndVersionComponents(true /* sanitized */);
449     return "::" + base::Join(components, "::");
450 }
451 
cppLocalName() const452 std::string FQName::cppLocalName() const {
453     std::vector<std::string> components = base::Split(mName, ".");
454 
455     return base::Join(components, "::")
456             + (mValueName.empty() ? "" : ("::" + mValueName));
457 }
458 
cppName() const459 std::string FQName::cppName() const {
460     std::string out = cppNamespace();
461 
462     std::vector<std::string> components = base::Split(name(), ".");
463     out += "::";
464     out += base::Join(components, "::");
465     if (!mValueName.empty()) {
466         out  += "::" + mValueName;
467     }
468 
469     return out;
470 }
471 
javaPackage() const472 std::string FQName::javaPackage() const {
473     std::vector<std::string> components = getPackageAndVersionComponents(true /* sanitized */);
474     return base::Join(components, ".");
475 }
476 
javaName() const477 std::string FQName::javaName() const {
478     return javaPackage() + "." + name()
479             + (mValueName.empty() ? "" : ("." + mValueName));
480 }
481 
getPackageComponents() const482 std::vector<std::string> FQName::getPackageComponents() const {
483     return base::Split(package(), ".");
484 }
485 
getPackageAndVersionComponents(bool sanitized) const486 std::vector<std::string> FQName::getPackageAndVersionComponents(bool sanitized) const {
487     CHECK(hasVersion()) << string() << ": getPackageAndVersionComponents expects version.";
488 
489     std::vector<std::string> components = getPackageComponents();
490     if (sanitized) {
491         components.push_back(sanitizedVersion());
492     } else {
493         components.push_back(version());
494     }
495     return components;
496 }
497 
hasVersion() const498 bool FQName::hasVersion() const {
499     return mMajor > 0;
500 }
501 
getVersion() const502 std::pair<size_t, size_t> FQName::getVersion() const {
503     return {mMajor, mMinor};
504 }
505 
withVersion(size_t major,size_t minor) const506 FQName FQName::withVersion(size_t major, size_t minor) const {
507     FQName ret(*this);
508     ret.mMajor = major;
509     ret.mMinor = minor;
510     return ret;
511 }
512 
getPackageMajorVersion() const513 size_t FQName::getPackageMajorVersion() const {
514     CHECK(hasVersion()) << "FQName: No version exists at getPackageMajorVersion(). "
515                         << "Did you check hasVersion()?";
516     return mMajor;
517 }
518 
getPackageMinorVersion() const519 size_t FQName::getPackageMinorVersion() const {
520     CHECK(hasVersion()) << "FQName: No version exists at getPackageMinorVersion(). "
521                         << "Did you check hasVersion()?";
522     return mMinor;
523 }
524 
endsWith(const FQName & other) const525 bool FQName::endsWith(const FQName &other) const {
526     std::string s1 = string();
527     std::string s2 = other.string();
528 
529     size_t pos = s1.rfind(s2);
530     if (pos == std::string::npos || pos + s2.size() != s1.size()) {
531         return false;
532     }
533 
534     // A match is only a match if it is preceded by a "boundary", i.e.
535     // we perform a component-wise match from the end.
536     // "az" is not a match for "android.hardware.foo@1.0::IFoo.bar.baz",
537     // "baz", "bar.baz", "IFoo.bar.baz", "@1.0::IFoo.bar.baz" are.
538     if (pos == 0) {
539         // matches "android.hardware.foo@1.0::IFoo.bar.baz"
540         return true;
541     }
542 
543     if (s1[pos - 1] == '.') {
544         // matches "baz" and "bar.baz"
545         return true;
546     }
547 
548     if (s1[pos - 1] == ':') {
549         // matches "IFoo.bar.baz"
550         return true;
551     }
552 
553     if (s1[pos] == '@') {
554         // matches "@1.0::IFoo.bar.baz"
555         return true;
556     }
557 
558     return false;
559 }
560 
inPackage(const std::string & package) const561 bool FQName::inPackage(const std::string &package) const {
562     std::vector<std::string> components = getPackageComponents();
563     std::vector<std::string> inComponents = base::Split(package, ".");
564 
565     if (inComponents.size() > components.size()) {
566         return false;
567     }
568 
569     for (size_t i = 0; i < inComponents.size(); i++) {
570         if (inComponents[i] != components[i]) {
571             return false;
572         }
573     }
574 
575     return true;
576 }
577 
downRev() const578 FQName FQName::downRev() const {
579     FQName ret(*this);
580     CHECK(ret.mMinor > 0);
581     ret.mMinor--;
582     return ret;
583 }
584 
upRev() const585 FQName FQName::upRev() const {
586     FQName ret(*this);
587     ret.mMinor++;
588     CHECK(ret.mMinor > 0);
589     return ret;
590 }
591 
592 }  // namespace android
593 
594