C++程序  |  529行  |  13.58 KB

/*
 * 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 <android-base/logging.h>
#include <android-base/parseint.h>
#include <iostream>
#include <regex>
#include <sstream>

#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<std::string> &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<std::string> FQName::names() const {
    std::vector<std::string> 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<std::string> components;
    getPackageAndVersionComponents(&components, true /* cpp_compatible */);

    if (!mName.empty()) {
        std::vector<std::string> nameComponents;
        StringHelper::SplitString(mName, '.', &nameComponents);

        components.insert(components.end(), nameComponents.begin(), nameComponents.end());
    }

    return StringHelper::JoinStrings(components, "_");
}

std::string FQName::cppNamespace() const {
    std::vector<std::string> components;
    getPackageAndVersionComponents(&components, true /* cpp_compatible */);

    std::string out = "::";
    out += StringHelper::JoinStrings(components, "::");

    return out;
}

std::string FQName::cppLocalName() const {
    std::vector<std::string> components;
    StringHelper::SplitString(mName, '.', &components);

    return StringHelper::JoinStrings(components, "::")
            + (mValueName.empty() ? "" : ("::" + mValueName));
}

std::string FQName::cppName() const {
    std::string out = cppNamespace();

    std::vector<std::string> components;
    StringHelper::SplitString(name(), '.', &components);
    out += "::";
    out += StringHelper::JoinStrings(components, "::");
    if (!mValueName.empty()) {
        out  += "::" + mValueName;
    }

    return out;
}

std::string FQName::javaPackage() const {
    std::vector<std::string> 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<std::string> *components) const {
    StringHelper::SplitString(package(), '.', components);
}

void FQName::getPackageAndVersionComponents(
        std::vector<std::string> *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<std::string> components;
    getPackageComponents(&components);

    std::vector<std::string> 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