| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 |
| * |
| * https://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 <algorithm> |
| #include <cmath> |
| #include <unordered_set> |
| |
| #include "Node.hh" |
| |
| namespace avro { |
| |
| using std::string; |
| |
| Node::~Node() = default; |
| |
| struct Name::Aliases { |
| std::vector<std::string> raw; |
| std::unordered_set<std::string> fullyQualified; |
| }; |
| |
| Name::Name() = default; |
| |
| Name::Name(const std::string &name) { |
| fullname(name); |
| } |
| |
| Name::Name(std::string simpleName, std::string ns) : ns_(std::move(ns)), simpleName_(std::move(simpleName)) { |
| check(); |
| } |
| |
| Name::Name(const Name &other) { |
| *this = other; |
| } |
| |
| Name &Name::operator=(const Name &other) { |
| if (this != &other) { |
| ns_ = other.ns_; |
| simpleName_ = other.simpleName_; |
| if (other.aliases_) { |
| aliases_ = std::make_unique<Aliases>(*other.aliases_); |
| } |
| } |
| return *this; |
| } |
| |
| Name::Name(Name &&other) = default; |
| |
| Name &Name::operator=(Name &&other) = default; |
| |
| Name::~Name() = default; |
| |
| string Name::fullname() const { |
| return ns_.empty() ? simpleName_ : ns_ + "." + simpleName_; |
| } |
| |
| void Name::fullname(const string &name) { |
| string::size_type n = name.find_last_of('.'); |
| if (n == string::npos) { |
| simpleName_ = name; |
| ns_.clear(); |
| } else { |
| ns_ = name.substr(0, n); |
| simpleName_ = name.substr(n + 1); |
| } |
| check(); |
| } |
| |
| const std::vector<std::string> &Name::aliases() const { |
| static const std::vector<std::string> emptyAliases; |
| return aliases_ ? aliases_->raw : emptyAliases; |
| } |
| |
| void Name::addAlias(const std::string &alias) { |
| if (!aliases_) { |
| aliases_ = std::make_unique<Aliases>(); |
| } |
| aliases_->raw.push_back(alias); |
| if (!ns_.empty() && alias.find_last_of('.') == string::npos) { |
| aliases_->fullyQualified.emplace(ns_ + "." + alias); |
| } else { |
| aliases_->fullyQualified.insert(alias); |
| } |
| } |
| |
| bool Name::operator<(const Name &n) const { |
| return (ns_ < n.ns_) || (!(n.ns_ < ns_) && (simpleName_ < n.simpleName_)); |
| } |
| |
| static bool invalidChar1(char c) { |
| return !isalnum(c) && c != '_' && c != '.' && c != '$'; |
| } |
| |
| static bool invalidChar2(char c) { |
| return !isalnum(c) && c != '_'; |
| } |
| |
| void Name::check() const { |
| if (!ns_.empty() && (ns_[0] == '.' || ns_[ns_.size() - 1] == '.' || std::find_if(ns_.begin(), ns_.end(), invalidChar1) != ns_.end())) { |
| throw Exception("Invalid namespace: " + ns_); |
| } |
| if (simpleName_.empty() |
| || std::find_if(simpleName_.begin(), simpleName_.end(), invalidChar2) != simpleName_.end()) { |
| throw Exception("Invalid name: " + simpleName_); |
| } |
| } |
| |
| bool Name::operator==(const Name &n) const { |
| return ns_ == n.ns_ && simpleName_ == n.simpleName_; |
| } |
| |
| bool Name::equalOrAliasedBy(const Name &n) const { |
| return *this == n || (n.aliases_ && n.aliases_->fullyQualified.find(fullname()) != n.aliases_->fullyQualified.end()); |
| } |
| |
| void Name::clear() { |
| ns_.clear(); |
| simpleName_.clear(); |
| aliases_.reset(); |
| } |
| |
| void Node::setLogicalType(LogicalType logicalType) { |
| checkLock(); |
| |
| // Check that the logical type is applicable to the node type. |
| switch (logicalType.type()) { |
| case LogicalType::NONE: break; |
| case LogicalType::BIG_DECIMAL: { |
| if (type_ != AVRO_BYTES) { |
| throw Exception("BIG_DECIMAL logical type can annotate " |
| "only BYTES type"); |
| } |
| break; |
| } |
| case LogicalType::DECIMAL: { |
| if (type_ != AVRO_BYTES && type_ != AVRO_FIXED) { |
| throw Exception("DECIMAL logical type can annotate " |
| "only BYTES or FIXED type"); |
| } |
| if (type_ == AVRO_FIXED) { |
| // Max precision that can be supported by the current size of |
| // the FIXED type. |
| auto maxPrecision = static_cast<int32_t>(floor(log10(2.0) * (8.0 * static_cast<double>(fixedSize()) - 1))); |
| if (logicalType.precision() > maxPrecision) { |
| throw Exception( |
| "DECIMAL precision {} is too large for the " |
| "FIXED type of size {}, precision cannot be " |
| "larger than {}", |
| logicalType.precision(), fixedSize(), maxPrecision); |
| } |
| } |
| if (logicalType.scale() > logicalType.precision()) { |
| throw Exception("DECIMAL scale cannot exceed precision"); |
| } |
| break; |
| } |
| case LogicalType::DATE: |
| if (type_ != AVRO_INT) { |
| throw Exception("DATE logical type can only annotate INT type"); |
| } |
| break; |
| case LogicalType::TIME_MILLIS: |
| if (type_ != AVRO_INT) { |
| throw Exception("TIME-MILLIS logical type can only annotate " |
| "INT type"); |
| } |
| break; |
| case LogicalType::TIME_MICROS: |
| if (type_ != AVRO_LONG) { |
| throw Exception("TIME-MICROS logical type can only annotate " |
| "LONG type"); |
| } |
| break; |
| case LogicalType::TIMESTAMP_MILLIS: |
| if (type_ != AVRO_LONG) { |
| throw Exception("TIMESTAMP-MILLIS logical type can only annotate " |
| "LONG type"); |
| } |
| break; |
| case LogicalType::TIMESTAMP_MICROS: |
| if (type_ != AVRO_LONG) { |
| throw Exception("TIMESTAMP-MICROS logical type can only annotate " |
| "LONG type"); |
| } |
| break; |
| case LogicalType::TIMESTAMP_NANOS: |
| if (type_ != AVRO_LONG) { |
| throw Exception("TIMESTAMP-NANOS logical type can only annotate " |
| "LONG type"); |
| } |
| break; |
| case LogicalType::LOCAL_TIMESTAMP_MILLIS: |
| if (type_ != AVRO_LONG) { |
| throw Exception("LOCAL-TIMESTAMP-MILLIS logical type can only annotate " |
| "LONG type"); |
| } |
| break; |
| case LogicalType::LOCAL_TIMESTAMP_MICROS: |
| if (type_ != AVRO_LONG) { |
| throw Exception("LOCAL-TIMESTAMP-MICROS logical type can only annotate " |
| "LONG type"); |
| } |
| break; |
| case LogicalType::LOCAL_TIMESTAMP_NANOS: |
| if (type_ != AVRO_LONG) { |
| throw Exception("LOCAL-TIMESTAMP-NANOS logical type can only annotate " |
| "LONG type"); |
| } |
| break; |
| case LogicalType::DURATION: |
| if (type_ != AVRO_FIXED || fixedSize() != 12) { |
| throw Exception("DURATION logical type can only annotate " |
| "FIXED type of size 12"); |
| } |
| break; |
| case LogicalType::UUID: |
| if (type_ != AVRO_STRING && (type_ != AVRO_FIXED || fixedSize() != 16)) { |
| throw Exception("UUID logical type can only annotate " |
| "STRING type or FIXED type of length 16"); |
| } |
| break; |
| case LogicalType::CUSTOM: |
| if (logicalType.customLogicalType() == nullptr) { |
| throw Exception("CUSTOM logical type is not set"); |
| } |
| break; |
| } |
| |
| logicalType_ = logicalType; |
| } |
| |
| } // namespace avro |