123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581 |
- /*
- * 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.
- */
- #ifndef avro_NodeImpl_hh__
- #define avro_NodeImpl_hh__
- #include "Config.hh"
- #include "GenericDatum.hh"
- #include <iomanip>
- #include <iostream>
- #include <limits>
- #include <memory>
- #include <set>
- #include <sstream>
- #include <utility>
- #include "Node.hh"
- #include "NodeConcepts.hh"
- #include "CustomAttributes.hh"
- namespace avro {
- /// Implementation details for Node. NodeImpl represents all the avro types,
- /// whose properties are enabled and disabled by selecting concept classes.
- template<
- class NameConcept,
- class LeavesConcept,
- class LeafNamesConcept,
- class MultiAttributesConcept,
- class SizeConcept>
- class NodeImpl : public Node {
- protected:
- explicit NodeImpl(Type type) : Node(type),
- nameAttribute_(),
- docAttribute_(),
- leafAttributes_(),
- leafNameAttributes_(),
- customAttributes_(),
- sizeAttribute_() {}
- NodeImpl(Type type,
- const NameConcept &name,
- const LeavesConcept &leaves,
- const LeafNamesConcept &leafNames,
- const MultiAttributesConcept &customAttributes,
- const SizeConcept &size) : Node(type),
- nameAttribute_(name),
- docAttribute_(),
- leafAttributes_(leaves),
- leafNameAttributes_(leafNames),
- customAttributes_(customAttributes),
- sizeAttribute_(size) {}
- // Ctor with "doc"
- NodeImpl(Type type,
- const NameConcept &name,
- const concepts::SingleAttribute<std::string> &doc,
- const LeavesConcept &leaves,
- const LeafNamesConcept &leafNames,
- const MultiAttributesConcept &customAttributes,
- const SizeConcept &size) : Node(type),
- nameAttribute_(name),
- docAttribute_(doc),
- leafAttributes_(leaves),
- leafNameAttributes_(leafNames),
- customAttributes_(customAttributes),
- sizeAttribute_(size) {}
- void swap(NodeImpl &impl) {
- std::swap(nameAttribute_, impl.nameAttribute_);
- std::swap(docAttribute_, impl.docAttribute_);
- std::swap(leafAttributes_, impl.leafAttributes_);
- std::swap(leafNameAttributes_, impl.leafNameAttributes_);
- std::swap(sizeAttribute_, impl.sizeAttribute_);
- std::swap(customAttributes_, impl.customAttributes_);
- std::swap(nameIndex_, impl.nameIndex_);
- }
- bool hasName() const override {
- // e.g.: true for single and multi-attributes, false for no-attributes.
- return NameConcept::hasAttribute;
- }
- void doSetName(const Name &name) override {
- nameAttribute_.add(name);
- }
- const Name &name() const override {
- return nameAttribute_.get();
- }
- void doSetDoc(const std::string &doc) override {
- docAttribute_.add(doc);
- }
- const std::string &getDoc() const override {
- return docAttribute_.get();
- }
- void doAddLeaf(const NodePtr &newLeaf) final {
- leafAttributes_.add(newLeaf);
- }
- size_t leaves() const override {
- return leafAttributes_.size();
- }
- const NodePtr &leafAt(size_t index) const override {
- return leafAttributes_.get(index);
- }
- void doAddName(const std::string &name) override {
- if (!nameIndex_.add(name, leafNameAttributes_.size())) {
- throw Exception(boost::format("Cannot add duplicate name: %1%") % name);
- }
- leafNameAttributes_.add(name);
- }
- size_t names() const override {
- return leafNameAttributes_.size();
- }
- const std::string &nameAt(size_t index) const override {
- return leafNameAttributes_.get(index);
- }
- bool nameIndex(const std::string &name, size_t &index) const override {
- return nameIndex_.lookup(name, index);
- }
- void doSetFixedSize(size_t size) override {
- sizeAttribute_.add(size);
- }
- size_t fixedSize() const override {
- return sizeAttribute_.get();
- }
- bool isValid() const override = 0;
- void printBasicInfo(std::ostream &os) const override;
- void setLeafToSymbolic(size_t index, const NodePtr &node) override;
- void doAddCustomAttribute(const CustomAttributes &customAttributes) override {
- customAttributes_.add(customAttributes);
- }
- SchemaResolution furtherResolution(const Node &reader) const {
- SchemaResolution match = RESOLVE_NO_MATCH;
- if (reader.type() == AVRO_SYMBOLIC) {
- // resolve the symbolic type, and check again
- const NodePtr &node = reader.leafAt(0);
- match = resolve(*node);
- } else if (reader.type() == AVRO_UNION) {
- // in this case, need to see if there is an exact match for the
- // writer's type, or if not, the first one that can be promoted to a
- // match
- for (size_t i = 0; i < reader.leaves(); ++i) {
- const NodePtr &node = reader.leafAt(i);
- SchemaResolution thisMatch = resolve(*node);
- // if matched then the search is done
- if (thisMatch == RESOLVE_MATCH) {
- match = thisMatch;
- break;
- }
- // thisMatch is either no match, or promotable, this will set match to
- // promotable if it hasn't been set already
- if (match == RESOLVE_NO_MATCH) {
- match = thisMatch;
- }
- }
- }
- return match;
- }
- NameConcept nameAttribute_;
- // Rem: NameConcept type is HasName (= SingleAttribute<Name>), we use std::string instead
- concepts::SingleAttribute<std::string> docAttribute_; /** Doc used to compare schemas */
- LeavesConcept leafAttributes_;
- LeafNamesConcept leafNameAttributes_;
- MultiAttributesConcept customAttributes_;
- SizeConcept sizeAttribute_;
- concepts::NameIndexConcept<LeafNamesConcept> nameIndex_;
- };
- using NoName = concepts::NoAttribute<Name>;
- using HasName = concepts::SingleAttribute<Name>;
- using HasDoc = concepts::SingleAttribute<std::string>;
- using NoLeaves = concepts::NoAttribute<NodePtr>;
- using SingleLeaf = concepts::SingleAttribute<NodePtr>;
- using MultiLeaves = concepts::MultiAttribute<NodePtr>;
- using NoLeafNames = concepts::NoAttribute<std::string>;
- using LeafNames = concepts::MultiAttribute<std::string>;
- using MultiAttributes = concepts::MultiAttribute<CustomAttributes>;
- using NoAttributes = concepts::NoAttribute<CustomAttributes>;
- using NoSize = concepts::NoAttribute<int>;
- using HasSize = concepts::SingleAttribute<int>;
- using NodeImplPrimitive = NodeImpl<NoName, NoLeaves, NoLeafNames, MultiAttributes, NoSize>;
- using NodeImplSymbolic = NodeImpl<HasName, NoLeaves, NoLeafNames, NoAttributes, NoSize>;
- using NodeImplRecord = NodeImpl<HasName, MultiLeaves, LeafNames, MultiAttributes, NoSize>;
- using NodeImplEnum = NodeImpl<HasName, NoLeaves, LeafNames, NoAttributes, NoSize>;
- using NodeImplArray = NodeImpl<NoName, SingleLeaf, NoLeafNames, NoAttributes, NoSize>;
- using NodeImplMap = NodeImpl<NoName, MultiLeaves, NoLeafNames, NoAttributes, NoSize>;
- using NodeImplUnion = NodeImpl<NoName, MultiLeaves, NoLeafNames, NoAttributes, NoSize>;
- using NodeImplFixed = NodeImpl<HasName, NoLeaves, NoLeafNames, NoAttributes, HasSize>;
- class AVRO_DECL NodePrimitive : public NodeImplPrimitive {
- public:
- explicit NodePrimitive(Type type) : NodeImplPrimitive(type) {}
- SchemaResolution resolve(const Node &reader) const override;
- void printJson(std::ostream &os, size_t depth) const override;
- bool isValid() const override {
- return true;
- }
- void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
- };
- class AVRO_DECL NodeSymbolic : public NodeImplSymbolic {
- using NodeWeakPtr = std::weak_ptr<Node>;
- public:
- NodeSymbolic() : NodeImplSymbolic(AVRO_SYMBOLIC) {}
- explicit NodeSymbolic(const HasName &name) : NodeImplSymbolic(AVRO_SYMBOLIC, name, NoLeaves(), NoLeafNames(), NoAttributes(), NoSize()) {}
- NodeSymbolic(const HasName &name, const NodePtr &n) : NodeImplSymbolic(AVRO_SYMBOLIC, name, NoLeaves(), NoLeafNames(), NoAttributes(), NoSize()), actualNode_(n) {}
- SchemaResolution resolve(const Node &reader) const override;
- void printJson(std::ostream &os, size_t depth) const override;
- bool isValid() const override {
- return (nameAttribute_.size() == 1);
- }
- void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
- bool isSet() const {
- return (actualNode_.lock() != nullptr);
- }
- NodePtr getNode() const {
- NodePtr node = actualNode_.lock();
- if (!node) {
- throw Exception(boost::format("Could not follow symbol %1%") % name());
- }
- return node;
- }
- void setNode(const NodePtr &node) {
- actualNode_ = node;
- }
- protected:
- NodeWeakPtr actualNode_;
- };
- class AVRO_DECL NodeRecord : public NodeImplRecord {
- std::vector<GenericDatum> defaultValues;
- public:
- NodeRecord() : NodeImplRecord(AVRO_RECORD) {}
- NodeRecord(const HasName &name, const MultiLeaves &fields,
- const LeafNames &fieldsNames,
- std::vector<GenericDatum> dv);
- NodeRecord(const HasName &name, const HasDoc &doc, const MultiLeaves &fields,
- const LeafNames &fieldsNames,
- std::vector<GenericDatum> dv) : NodeImplRecord(AVRO_RECORD, name, doc, fields, fieldsNames, MultiAttributes(), NoSize()),
- defaultValues(std::move(dv)) {
- leafNameCheck();
- }
- NodeRecord(const HasName &name, const MultiLeaves &fields,
- const LeafNames &fieldsNames,
- const std::vector<GenericDatum>& dv,
- const MultiAttributes &customAttributes) :
- NodeImplRecord(AVRO_RECORD, name, fields, fieldsNames, customAttributes, NoSize()),
- defaultValues(dv) {
- leafNameCheck();
- }
- NodeRecord(const HasName &name, const HasDoc &doc, const MultiLeaves &fields,
- const LeafNames &fieldsNames,
- const std::vector<GenericDatum>& dv,
- const MultiAttributes &customAttributes) :
- NodeImplRecord(AVRO_RECORD, name, doc, fields, fieldsNames, customAttributes, NoSize()),
- defaultValues(dv) {
- leafNameCheck();
- }
- void swap(NodeRecord &r) {
- NodeImplRecord::swap(r);
- defaultValues.swap(r.defaultValues);
- }
- SchemaResolution resolve(const Node &reader) const override;
- void printJson(std::ostream &os, size_t depth) const override;
- bool isValid() const override {
- return ((nameAttribute_.size() == 1) &&
- (leafAttributes_.size() == leafNameAttributes_.size()) &&
- (customAttributes_.size() == 0 ||
- customAttributes_.size() == leafAttributes_.size()));
- }
- const GenericDatum &defaultValueAt(size_t index) override {
- return defaultValues[index];
- }
- void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
- private:
- // check if leaf name is valid Name and is not duplicate
- void leafNameCheck() {
- for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
- if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
- throw Exception(boost::format(
- "Cannot add duplicate field: %1%")
- % leafNameAttributes_.get(i));
- }
- }
- }
- };
- class AVRO_DECL NodeEnum : public NodeImplEnum {
- public:
- NodeEnum() : NodeImplEnum(AVRO_ENUM) {}
- NodeEnum(const HasName &name, const LeafNames &symbols) : NodeImplEnum(AVRO_ENUM, name, NoLeaves(), symbols, NoAttributes(), NoSize()) {
- for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
- if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
- throw Exception(boost::format("Cannot add duplicate enum: %1%") % leafNameAttributes_.get(i));
- }
- }
- }
- SchemaResolution resolve(const Node &reader) const override;
- void printJson(std::ostream &os, size_t depth) const override;
- bool isValid() const override {
- return (
- (nameAttribute_.size() == 1) && (leafNameAttributes_.size() > 0));
- }
- void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
- };
- class AVRO_DECL NodeArray : public NodeImplArray {
- public:
- NodeArray() : NodeImplArray(AVRO_ARRAY) {}
- explicit NodeArray(const SingleLeaf &items) : NodeImplArray(AVRO_ARRAY, NoName(), items, NoLeafNames(), NoAttributes(), NoSize()) {}
- SchemaResolution resolve(const Node &reader) const override;
- void printJson(std::ostream &os, size_t depth) const override;
- bool isValid() const override {
- return (leafAttributes_.size() == 1);
- }
- void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
- };
- class AVRO_DECL NodeMap : public NodeImplMap {
- public:
- NodeMap();
- explicit NodeMap(const SingleLeaf &values) : NodeImplMap(AVRO_MAP, NoName(), MultiLeaves(values), NoLeafNames(), NoAttributes(), NoSize()) {
- // need to add the key for the map too
- NodePtr key(new NodePrimitive(AVRO_STRING));
- doAddLeaf(key);
- // key goes before value
- std::swap(leafAttributes_.get(0), leafAttributes_.get(1));
- }
- SchemaResolution resolve(const Node &reader) const override;
- void printJson(std::ostream &os, size_t depth) const override;
- bool isValid() const override {
- return (leafAttributes_.size() == 2);
- }
- void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
- };
- class AVRO_DECL NodeUnion : public NodeImplUnion {
- public:
- NodeUnion() : NodeImplUnion(AVRO_UNION) {}
- explicit NodeUnion(const MultiLeaves &types) : NodeImplUnion(AVRO_UNION, NoName(), types, NoLeafNames(), NoAttributes(), NoSize()) {}
- SchemaResolution resolve(const Node &reader) const override;
- void printJson(std::ostream &os, size_t depth) const override;
- bool isValid() const override {
- std::set<std::string> seen;
- if (leafAttributes_.size() >= 1) {
- for (size_t i = 0; i < leafAttributes_.size(); ++i) {
- std::string name;
- const NodePtr &n = leafAttributes_.get(i);
- switch (n->type()) {
- case AVRO_STRING:
- name = "string";
- break;
- case AVRO_BYTES:
- name = "bytes";
- break;
- case AVRO_INT:
- name = "int";
- break;
- case AVRO_LONG:
- name = "long";
- break;
- case AVRO_FLOAT:
- name = "float";
- break;
- case AVRO_DOUBLE:
- name = "double";
- break;
- case AVRO_BOOL:
- name = "bool";
- break;
- case AVRO_NULL:
- name = "null";
- break;
- case AVRO_ARRAY:
- name = "array";
- break;
- case AVRO_MAP:
- name = "map";
- break;
- case AVRO_RECORD:
- case AVRO_ENUM:
- case AVRO_UNION:
- case AVRO_FIXED:
- case AVRO_SYMBOLIC:
- name = n->name().fullname();
- break;
- default: return false;
- }
- if (seen.find(name) != seen.end()) {
- return false;
- }
- seen.insert(name);
- }
- return true;
- }
- return false;
- }
- void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
- };
- class AVRO_DECL NodeFixed : public NodeImplFixed {
- public:
- NodeFixed() : NodeImplFixed(AVRO_FIXED) {}
- NodeFixed(const HasName &name, const HasSize &size) : NodeImplFixed(AVRO_FIXED, name, NoLeaves(), NoLeafNames(), NoAttributes(), size) {}
- SchemaResolution resolve(const Node &reader) const override;
- void printJson(std::ostream &os, size_t depth) const override;
- bool isValid() const override {
- return (
- (nameAttribute_.size() == 1) && (sizeAttribute_.size() == 1));
- }
- void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
- };
- template<class A, class B, class C, class D, class E>
- inline void
- NodeImpl<A, B, C, D, E>::setLeafToSymbolic(size_t index, const NodePtr &node) {
- if (!B::hasAttribute) {
- throw Exception("Cannot change leaf node for nonexistent leaf");
- }
- auto &replaceNode = const_cast<NodePtr &>(leafAttributes_.get(index));
- if (replaceNode->name() != node->name()) {
- throw Exception("Symbolic name does not match the name of the schema it references");
- }
- auto symbol = std::make_shared<NodeSymbolic>();
- symbol->setName(node->name());
- symbol->setNode(node);
- replaceNode = symbol;
- }
- template<class A, class B, class C, class D, class E>
- inline void
- NodeImpl<A, B, C, D, E>::printBasicInfo(std::ostream &os) const {
- os << type();
- if (hasName()) {
- os << ' ' << nameAttribute_.get();
- }
- if (E::hasAttribute) {
- os << " " << sizeAttribute_.get();
- }
- os << '\n';
- size_t count = leaves();
- count = count ? count : names();
- for (size_t i = 0; i < count; ++i) {
- if (C::hasAttribute) {
- os << "name " << nameAt(i) << '\n';
- }
- if (type() != AVRO_SYMBOLIC && leafAttributes_.hasAttribute) {
- leafAt(i)->printBasicInfo(os);
- }
- }
- if (isCompound(type())) {
- os << "end " << type() << '\n';
- }
- }
- inline NodePtr resolveSymbol(const NodePtr &node) {
- if (node->type() != AVRO_SYMBOLIC) {
- throw Exception("Only symbolic nodes may be resolved");
- }
- std::shared_ptr<NodeSymbolic> symNode = std::static_pointer_cast<NodeSymbolic>(node);
- return symNode->getNode();
- }
- template<typename T>
- inline std::string intToHex(T i) {
- std::stringstream stream;
- stream << "\\u"
- << std::setfill('0') << std::setw(sizeof(T))
- << std::hex << i;
- return stream.str();
- }
- } // namespace avro
- #endif
|