/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "FQName.h" #include "StringHelper.h" #include #include #include #include #include #define RE_COMPONENT "[a-zA-Z_][a-zA-Z_0-9]*" #define RE_PATH RE_COMPONENT "(?:[.]" RE_COMPONENT ")*" #define RE_MAJOR "[0-9]+" #define RE_MINOR "[0-9]+" // android.hardware.foo@1.0::IFoo.Type static const std::regex kRE1("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")"); // @1.0::IFoo.Type static const std::regex kRE2("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH ")"); // android.hardware.foo@1.0 (for package declaration and whole package import) static const std::regex kRE3("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")"); // IFoo.Type static const std::regex kRE4("(" RE_COMPONENT ")([.]" RE_COMPONENT ")+"); // Type (a plain identifier) static const std::regex kRE5("(" RE_COMPONENT ")"); // android.hardware.foo@1.0::IFoo.Type:MY_ENUM_VALUE static const std::regex kRE6("(" RE_PATH ")@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH "):(" RE_COMPONENT ")"); // @1.0::IFoo.Type:MY_ENUM_VALUE static const std::regex kRE7("@(" RE_MAJOR ")[.](" RE_MINOR ")::(" RE_PATH "):(" RE_COMPONENT ")"); // IFoo.Type:MY_ENUM_VALUE static const std::regex kRE8("(" RE_PATH "):(" RE_COMPONENT ")"); // 1.0 static const std::regex kREVer("(" RE_MAJOR ")[.](" RE_MINOR ")"); namespace android { FQName::FQName() : mValid(false), mIsIdentifier(false) { } FQName::FQName(const std::string &s) : mValid(false), mIsIdentifier(false) { setTo(s); } FQName::FQName( const std::string &package, const std::string &version, const std::string &name, const std::string &valueName) : mValid(true), mIsIdentifier(false), mPackage(package), mName(name), mValueName(valueName) { setVersion(version); // Check if this is actually a valid fqName FQName other; other.setTo(this->string()); CHECK(other.mValid && (*this) == other); } FQName::FQName(const FQName& other) : mValid(other.mValid), mIsIdentifier(other.mIsIdentifier), mPackage(other.mPackage), mMajor(other.mMajor), mMinor(other.mMinor), mName(other.mName), mValueName(other.mValueName) { } FQName::FQName(const std::vector &names) : mValid(false), mIsIdentifier(false) { setTo(StringHelper::JoinStrings(names, ".")); } bool FQName::isValid() const { return mValid; } bool FQName::isIdentifier() const { return mIsIdentifier; } bool FQName::isFullyQualified() const { return !mPackage.empty() && !version().empty() && !mName.empty(); } bool FQName::isValidValueName() const { return mIsIdentifier || (!mName.empty() && !mValueName.empty()); } bool FQName::setTo(const std::string &s) { clearVersion(); mPackage.clear(); mName.clear(); mValid = true; std::smatch match; if (std::regex_match(s, match, kRE1)) { CHECK_EQ(match.size(), 5u); mPackage = match.str(1); parseVersion(match.str(2), match.str(3)); mName = match.str(4); } else if (std::regex_match(s, match, kRE2)) { CHECK_EQ(match.size(), 4u); parseVersion(match.str(1), match.str(2)); mName = match.str(3); } else if (std::regex_match(s, match, kRE3)) { CHECK_EQ(match.size(), 4u); mPackage = match.str(1); parseVersion(match.str(2), match.str(3)); } else if (std::regex_match(s, match, kRE4)) { mName = match.str(0); } else if (std::regex_match(s, match, kRE5)) { mIsIdentifier = true; mName = match.str(0); } else if (std::regex_match(s, match, kRE6)) { CHECK_EQ(match.size(), 6u); mPackage = match.str(1); parseVersion(match.str(2), match.str(3)); mName = match.str(4); mValueName = match.str(5); } else if (std::regex_match(s, match, kRE7)) { CHECK_EQ(match.size(), 5u); parseVersion(match.str(1), match.str(2)); mName = match.str(3); mValueName = match.str(4); } else if (std::regex_match(s, match, kRE8)) { CHECK_EQ(match.size(), 3u); mName = match.str(1); mValueName = match.str(2); } else { mValid = false; } // mValueName must go with mName. CHECK(mValueName.empty() || !mName.empty()); // package without version is not allowed. CHECK(mPackage.empty() || !version().empty()); return isValid(); } std::string FQName::package() const { return mPackage; } std::string FQName::version() const { if (!hasVersion()) { return ""; } return std::to_string(mMajor) + "." + std::to_string(mMinor); } std::string FQName::sanitizedVersion() const { if (!hasVersion()) { return ""; } return "V" + std::to_string(mMajor) + "_" + std::to_string(mMinor); } std::string FQName::atVersion() const { std::string v = version(); return v.empty() ? "" : ("@" + v); } void FQName::setVersion(const std::string &v) { if (v.empty()) { clearVersion(); return; } std::smatch match; if (std::regex_match(v, match, kREVer)) { CHECK_EQ(match.size(), 3u); parseVersion(match.str(1), match.str(2)); } else { mValid = false; } } void FQName::clearVersion() { mMajor = mMinor = 0; } void FQName::parseVersion(const std::string &majorStr, const std::string &minorStr) { bool versionParseSuccess = ::android::base::ParseUint(majorStr, &mMajor) && ::android::base::ParseUint(minorStr, &mMinor); if (!versionParseSuccess) { LOG(ERROR) << "numbers in " << majorStr << "." << minorStr << " are out of range."; mValid = false; } } std::string FQName::name() const { return mName; } std::vector FQName::names() const { std::vector res {}; std::istringstream ss(name()); std::string s; while (std::getline(ss, s, '.')) { res.push_back(s); } return res; } std::string FQName::valueName() const { return mValueName; } FQName FQName::typeName() const { return FQName(mPackage, version(), mName); } void FQName::applyDefaults( const std::string &defaultPackage, const std::string &defaultVersion) { // package without version is not allowed. CHECK(mPackage.empty() || !version().empty()); if (mPackage.empty()) { mPackage = defaultPackage; } if (version().empty()) { setVersion(defaultVersion); } } std::string FQName::string() const { CHECK(mValid); std::string out; out.append(mPackage); out.append(atVersion()); if (!mName.empty()) { if (!mPackage.empty() || !version().empty()) { out.append("::"); } out.append(mName); if (!mValueName.empty()) { out.append(":"); out.append(mValueName); } } return out; } void FQName::print() const { if (!mValid) { LOG(INFO) << "INVALID"; return; } LOG(INFO) << string(); } bool FQName::operator<(const FQName &other) const { return string() < other.string(); } bool FQName::operator==(const FQName &other) const { return string() == other.string(); } bool FQName::operator!=(const FQName &other) const { return !(*this == other); } std::string FQName::getInterfaceName() const { CHECK(names().size() == 1) << "Must be a top level type"; CHECK(!mName.empty() && mName[0] == 'I') << mName; return mName; } std::string FQName::getInterfaceBaseName() const { // cut off the leading 'I'. return getInterfaceName().substr(1); } std::string FQName::getInterfaceHwName() const { return "IHw" + getInterfaceBaseName(); } std::string FQName::getInterfaceProxyName() const { return "BpHw" + getInterfaceBaseName(); } std::string FQName::getInterfaceStubName() const { return "BnHw" + getInterfaceBaseName(); } std::string FQName::getInterfacePassthroughName() const { return "Bs" + getInterfaceBaseName(); } FQName FQName::getInterfaceProxyFqName() const { return FQName(package(), version(), getInterfaceProxyName()); } FQName FQName::getInterfaceStubFqName() const { return FQName(package(), version(), getInterfaceStubName()); } FQName FQName::getInterfacePassthroughFqName() const { return FQName(package(), version(), getInterfacePassthroughName()); } FQName FQName::getTypesForPackage() const { return FQName(package(), version(), "types"); } FQName FQName::getPackageAndVersion() const { return FQName(package(), version(), ""); } FQName FQName::getTopLevelType() const { auto idx = mName.find('.'); if (idx == std::string::npos) { return *this; } return FQName(mPackage, version(), mName.substr(0, idx)); } std::string FQName::tokenName() const { std::vector components; getPackageAndVersionComponents(&components, true /* cpp_compatible */); if (!mName.empty()) { std::vector nameComponents; StringHelper::SplitString(mName, '.', &nameComponents); components.insert(components.end(), nameComponents.begin(), nameComponents.end()); } return StringHelper::JoinStrings(components, "_"); } std::string FQName::cppNamespace() const { std::vector components; getPackageAndVersionComponents(&components, true /* cpp_compatible */); std::string out = "::"; out += StringHelper::JoinStrings(components, "::"); return out; } std::string FQName::cppLocalName() const { std::vector components; StringHelper::SplitString(mName, '.', &components); return StringHelper::JoinStrings(components, "::") + (mValueName.empty() ? "" : ("::" + mValueName)); } std::string FQName::cppName() const { std::string out = cppNamespace(); std::vector components; StringHelper::SplitString(name(), '.', &components); out += "::"; out += StringHelper::JoinStrings(components, "::"); if (!mValueName.empty()) { out += "::" + mValueName; } return out; } std::string FQName::javaPackage() const { std::vector components; getPackageAndVersionComponents(&components, true /* cpp_compatible */); return StringHelper::JoinStrings(components, "."); } std::string FQName::javaName() const { return javaPackage() + "." + name() + (mValueName.empty() ? "" : ("." + mValueName)); } void FQName::getPackageComponents(std::vector *components) const { StringHelper::SplitString(package(), '.', components); } void FQName::getPackageAndVersionComponents( std::vector *components, bool cpp_compatible) const { getPackageComponents(components); if (!hasVersion()) { LOG(WARNING) << "FQName: getPackageAndVersionComponents expects version."; return; } if (!cpp_compatible) { components->push_back(std::to_string(getPackageMajorVersion()) + "." + std::to_string(getPackageMinorVersion())); return; } components->push_back(sanitizedVersion()); } bool FQName::hasVersion() const { return mMajor > 0; } size_t FQName::getPackageMajorVersion() const { CHECK(hasVersion()) << "FQName: No version exists at getPackageMajorVersion(). " << "Did you check hasVersion()?"; return mMajor; } size_t FQName::getPackageMinorVersion() const { CHECK(hasVersion()) << "FQName: No version exists at getPackageMinorVersion(). " << "Did you check hasVersion()?"; return mMinor; } bool FQName::endsWith(const FQName &other) const { std::string s1 = string(); std::string s2 = other.string(); size_t pos = s1.rfind(s2); if (pos == std::string::npos || pos + s2.size() != s1.size()) { return false; } // A match is only a match if it is preceded by a "boundary", i.e. // we perform a component-wise match from the end. // "az" is not a match for "android.hardware.foo@1.0::IFoo.bar.baz", // "baz", "bar.baz", "IFoo.bar.baz", "@1.0::IFoo.bar.baz" are. if (pos == 0) { // matches "android.hardware.foo@1.0::IFoo.bar.baz" return true; } if (s1[pos - 1] == '.') { // matches "baz" and "bar.baz" return true; } if (s1[pos - 1] == ':') { // matches "IFoo.bar.baz" return true; } if (s1[pos] == '@') { // matches "@1.0::IFoo.bar.baz" return true; } return false; } bool FQName::inPackage(const std::string &package) const { std::vector components; getPackageComponents(&components); std::vector inComponents; StringHelper::SplitString(package, '.', &inComponents); if (inComponents.size() > components.size()) { return false; } for (size_t i = 0; i < inComponents.size(); i++) { if (inComponents[i] != components[i]) { return false; } } return true; } FQName FQName::downRev() const { FQName ret(*this); CHECK(ret.mMinor > 0); ret.mMinor--; return ret; } } // namespace android