Avro C++
NodeImpl.hh
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * https://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #ifndef avro_NodeImpl_hh__
20 #define avro_NodeImpl_hh__
21 
22 #include "Config.hh"
23 #include "GenericDatum.hh"
24 
25 #include <iomanip>
26 #include <iostream>
27 #include <limits>
28 #include <memory>
29 #include <set>
30 #include <sstream>
31 #include <utility>
32 
33 #include "Node.hh"
34 #include "NodeConcepts.hh"
35 
36 namespace avro {
37 
40 
41 template<
42  class NameConcept,
43  class LeavesConcept,
44  class LeafNamesConcept,
45  class SizeConcept>
46 class NodeImpl : public Node {
47 
48 protected:
49  explicit NodeImpl(Type type) : Node(type),
50  nameAttribute_(),
51  docAttribute_(),
53  leafNameAttributes_(),
54  sizeAttribute_() {}
55 
56  NodeImpl(Type type,
57  const NameConcept &name,
58  const LeavesConcept &leaves,
59  const LeafNamesConcept &leafNames,
60  const SizeConcept &size) : Node(type),
61  nameAttribute_(name),
62  docAttribute_(),
63  leafAttributes_(leaves),
64  leafNameAttributes_(leafNames),
65  sizeAttribute_(size) {}
66 
67  // Ctor with "doc"
68  NodeImpl(Type type,
69  const NameConcept &name,
71  const LeavesConcept &leaves,
72  const LeafNamesConcept &leafNames,
73  const SizeConcept &size) : Node(type),
74  nameAttribute_(name),
75  docAttribute_(doc),
76  leafAttributes_(leaves),
77  leafNameAttributes_(leafNames),
78  sizeAttribute_(size) {}
79 
80  void swap(NodeImpl &impl) {
81  std::swap(nameAttribute_, impl.nameAttribute_);
82  std::swap(docAttribute_, impl.docAttribute_);
83  std::swap(leafAttributes_, impl.leafAttributes_);
84  std::swap(leafNameAttributes_, impl.leafNameAttributes_);
85  std::swap(sizeAttribute_, impl.sizeAttribute_);
86  std::swap(nameIndex_, impl.nameIndex_);
87  }
88 
89  bool hasName() const override {
90  // e.g.: true for single and multi-attributes, false for no-attributes.
91  return NameConcept::hasAttribute;
92  }
93 
94  void doSetName(const Name &name) override {
95  nameAttribute_.add(name);
96  }
97 
98  const Name &name() const override {
99  return nameAttribute_.get();
100  }
101 
102  void doSetDoc(const std::string &doc) override {
103  docAttribute_.add(doc);
104  }
105 
106  const std::string &getDoc() const override {
107  return docAttribute_.get();
108  }
109 
110  void doAddLeaf(const NodePtr &newLeaf) final {
111  leafAttributes_.add(newLeaf);
112  }
113 
114  size_t leaves() const override {
115  return leafAttributes_.size();
116  }
117 
118  const NodePtr &leafAt(size_t index) const override {
119  return leafAttributes_.get(index);
120  }
121 
122  void doAddName(const std::string &name) override {
123  if (!nameIndex_.add(name, leafNameAttributes_.size())) {
124  throw Exception(boost::format("Cannot add duplicate name: %1%") % name);
125  }
126  leafNameAttributes_.add(name);
127  }
128 
129  size_t names() const override {
130  return leafNameAttributes_.size();
131  }
132 
133  const std::string &nameAt(size_t index) const override {
134  return leafNameAttributes_.get(index);
135  }
136 
137  bool nameIndex(const std::string &name, size_t &index) const override {
138  return nameIndex_.lookup(name, index);
139  }
140 
141  void doSetFixedSize(size_t size) override {
142  sizeAttribute_.add(size);
143  }
144 
145  size_t fixedSize() const override {
146  return sizeAttribute_.get();
147  }
148 
149  bool isValid() const override = 0;
150 
151  void printBasicInfo(std::ostream &os) const override;
152 
153  void setLeafToSymbolic(size_t index, const NodePtr &node) override;
154 
155  SchemaResolution furtherResolution(const Node &reader) const {
157 
158  if (reader.type() == AVRO_SYMBOLIC) {
159 
160  // resolve the symbolic type, and check again
161  const NodePtr &node = reader.leafAt(0);
162  match = resolve(*node);
163  } else if (reader.type() == AVRO_UNION) {
164 
165  // in this case, need to see if there is an exact match for the
166  // writer's type, or if not, the first one that can be promoted to a
167  // match
168 
169  for (size_t i = 0; i < reader.leaves(); ++i) {
170 
171  const NodePtr &node = reader.leafAt(i);
172  SchemaResolution thisMatch = resolve(*node);
173 
174  // if matched then the search is done
175  if (thisMatch == RESOLVE_MATCH) {
176  match = thisMatch;
177  break;
178  }
179 
180  // thisMatch is either no match, or promotable, this will set match to
181  // promotable if it hasn't been set already
182  if (match == RESOLVE_NO_MATCH) {
183  match = thisMatch;
184  }
185  }
186  }
187 
188  return match;
189  }
190 
191  NameConcept nameAttribute_;
192 
193  // Rem: NameConcept type is HasName (= SingleAttribute<Name>), we use std::string instead
196  LeavesConcept leafAttributes_;
197  LeafNamesConcept leafNameAttributes_;
198  SizeConcept sizeAttribute_;
200 };
201 
204 
206 
210 
213 
216 
219 
226 
227 class AVRO_DECL NodePrimitive : public NodeImplPrimitive {
228 public:
229  explicit NodePrimitive(Type type) : NodeImplPrimitive(type) {}
230 
231  SchemaResolution resolve(const Node &reader) const override;
232 
233  void printJson(std::ostream &os, size_t depth) const override;
234 
235  bool isValid() const override {
236  return true;
237  }
238 
239  void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
240 };
241 
242 class AVRO_DECL NodeSymbolic : public NodeImplSymbolic {
243  using NodeWeakPtr = std::weak_ptr<Node>;
244 
245 public:
247 
248  explicit NodeSymbolic(const HasName &name) : NodeImplSymbolic(AVRO_SYMBOLIC, name, NoLeaves(), NoLeafNames(), NoSize()) {}
249 
250  NodeSymbolic(const HasName &name, const NodePtr &n) : NodeImplSymbolic(AVRO_SYMBOLIC, name, NoLeaves(), NoLeafNames(), NoSize()), actualNode_(n) {}
251  SchemaResolution resolve(const Node &reader) const override;
252 
253  void printJson(std::ostream &os, size_t depth) const override;
254 
255  bool isValid() const override {
256  return (nameAttribute_.size() == 1);
257  }
258 
259  void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
260 
261  bool isSet() const {
262  return (actualNode_.lock() != nullptr);
263  }
264 
265  NodePtr getNode() const {
266  NodePtr node = actualNode_.lock();
267  if (!node) {
268  throw Exception(boost::format("Could not follow symbol %1%") % name());
269  }
270  return node;
271  }
272 
273  void setNode(const NodePtr &node) {
274  actualNode_ = node;
275  }
276 
277 protected:
278  NodeWeakPtr actualNode_;
279 };
280 
281 class AVRO_DECL NodeRecord : public NodeImplRecord {
282  std::vector<GenericDatum> defaultValues;
283 
284 public:
286  NodeRecord(const HasName &name, const MultiLeaves &fields,
287  const LeafNames &fieldsNames,
288  std::vector<GenericDatum> dv);
289 
290  NodeRecord(const HasName &name, const HasDoc &doc, const MultiLeaves &fields,
291  const LeafNames &fieldsNames,
292  std::vector<GenericDatum> dv) : NodeImplRecord(AVRO_RECORD, name, doc, fields, fieldsNames, NoSize()),
293  defaultValues(std::move(dv)) {
294  for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
295  if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
296  throw Exception(boost::format(
297  "Cannot add duplicate field: %1%")
298  % leafNameAttributes_.get(i));
299  }
300  }
301  }
302 
303  void swap(NodeRecord &r) {
304  NodeImplRecord::swap(r);
305  defaultValues.swap(r.defaultValues);
306  }
307 
308  SchemaResolution resolve(const Node &reader) const override;
309 
310  void printJson(std::ostream &os, size_t depth) const override;
311 
312  bool isValid() const override {
313  return ((nameAttribute_.size() == 1) && (leafAttributes_.size() == leafNameAttributes_.size()));
314  }
315 
316  const GenericDatum &defaultValueAt(size_t index) override {
317  return defaultValues[index];
318  }
319 
320  void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
321 };
322 
323 class AVRO_DECL NodeEnum : public NodeImplEnum {
324 public:
326 
327  NodeEnum(const HasName &name, const LeafNames &symbols) : NodeImplEnum(AVRO_ENUM, name, NoLeaves(), symbols, NoSize()) {
328  for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
329  if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
330  throw Exception(boost::format("Cannot add duplicate enum: %1%") % leafNameAttributes_.get(i));
331  }
332  }
333  }
334 
335  SchemaResolution resolve(const Node &reader) const override;
336 
337  void printJson(std::ostream &os, size_t depth) const override;
338 
339  bool isValid() const override {
340  return (
341  (nameAttribute_.size() == 1) && (leafNameAttributes_.size() > 0));
342  }
343 
344  void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
345 };
346 
347 class AVRO_DECL NodeArray : public NodeImplArray {
348 public:
350 
351  explicit NodeArray(const SingleLeaf &items) : NodeImplArray(AVRO_ARRAY, NoName(), items, NoLeafNames(), NoSize()) {}
352 
353  SchemaResolution resolve(const Node &reader) const override;
354 
355  void printJson(std::ostream &os, size_t depth) const override;
356 
357  bool isValid() const override {
358  return (leafAttributes_.size() == 1);
359  }
360 
361  void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
362 };
363 
364 class AVRO_DECL NodeMap : public NodeImplMap {
365 public:
366  NodeMap();
367 
368  explicit NodeMap(const SingleLeaf &values) : NodeImplMap(AVRO_MAP, NoName(), MultiLeaves(values), NoLeafNames(), NoSize()) {
369  // need to add the key for the map too
370  NodePtr key(new NodePrimitive(AVRO_STRING));
371  doAddLeaf(key);
372 
373  // key goes before value
374  std::swap(leafAttributes_.get(0), leafAttributes_.get(1));
375  }
376 
377  SchemaResolution resolve(const Node &reader) const override;
378 
379  void printJson(std::ostream &os, size_t depth) const override;
380 
381  bool isValid() const override {
382  return (leafAttributes_.size() == 2);
383  }
384 
385  void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
386 };
387 
388 class AVRO_DECL NodeUnion : public NodeImplUnion {
389 public:
391 
392  explicit NodeUnion(const MultiLeaves &types) : NodeImplUnion(AVRO_UNION, NoName(), types, NoLeafNames(), NoSize()) {}
393 
394  SchemaResolution resolve(const Node &reader) const override;
395 
396  void printJson(std::ostream &os, size_t depth) const override;
397 
398  bool isValid() const override {
399  std::set<std::string> seen;
400  if (leafAttributes_.size() >= 1) {
401  for (size_t i = 0; i < leafAttributes_.size(); ++i) {
402  std::string name;
403  const NodePtr &n = leafAttributes_.get(i);
404  switch (n->type()) {
405  case AVRO_STRING:
406  name = "string";
407  break;
408  case AVRO_BYTES:
409  name = "bytes";
410  break;
411  case AVRO_INT:
412  name = "int";
413  break;
414  case AVRO_LONG:
415  name = "long";
416  break;
417  case AVRO_FLOAT:
418  name = "float";
419  break;
420  case AVRO_DOUBLE:
421  name = "double";
422  break;
423  case AVRO_BOOL:
424  name = "bool";
425  break;
426  case AVRO_NULL:
427  name = "null";
428  break;
429  case AVRO_ARRAY:
430  name = "array";
431  break;
432  case AVRO_MAP:
433  name = "map";
434  break;
435  case AVRO_RECORD:
436  case AVRO_ENUM:
437  case AVRO_UNION:
438  case AVRO_FIXED:
439  case AVRO_SYMBOLIC:
440  name = n->name().fullname();
441  break;
442  default: return false;
443  }
444  if (seen.find(name) != seen.end()) {
445  return false;
446  }
447  seen.insert(name);
448  }
449  return true;
450  }
451  return false;
452  }
453 
454  void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
455 };
456 
457 class AVRO_DECL NodeFixed : public NodeImplFixed {
458 public:
460 
461  NodeFixed(const HasName &name, const HasSize &size) : NodeImplFixed(AVRO_FIXED, name, NoLeaves(), NoLeafNames(), size) {}
462 
463  SchemaResolution resolve(const Node &reader) const override;
464 
465  void printJson(std::ostream &os, size_t depth) const override;
466 
467  bool isValid() const override {
468  return (
469  (nameAttribute_.size() == 1) && (sizeAttribute_.size() == 1));
470  }
471 
472  void printDefaultToJson(const GenericDatum &g, std::ostream &os, size_t depth) const override;
473 };
474 
475 template<class A, class B, class C, class D>
476 inline void
477 NodeImpl<A, B, C, D>::setLeafToSymbolic(size_t index, const NodePtr &node) {
478  if (!B::hasAttribute) {
479  throw Exception("Cannot change leaf node for nonexistent leaf");
480  }
481 
482  auto &replaceNode = const_cast<NodePtr &>(leafAttributes_.get(index));
483  if (replaceNode->name() != node->name()) {
484  throw Exception("Symbolic name does not match the name of the schema it references");
485  }
486 
487  auto symbol = std::make_shared<NodeSymbolic>();
488  symbol->setName(node->name());
489  symbol->setNode(node);
490  replaceNode = symbol;
491 }
492 
493 template<class A, class B, class C, class D>
494 inline void
495 NodeImpl<A, B, C, D>::printBasicInfo(std::ostream &os) const {
496  os << type();
497  if (hasName()) {
498  os << ' ' << nameAttribute_.get();
499  }
500 
501  if (D::hasAttribute) {
502  os << " " << sizeAttribute_.get();
503  }
504  os << '\n';
505  int count = leaves();
506  count = count ? count : names();
507  for (int i = 0; i < count; ++i) {
508  if (C::hasAttribute) {
509  os << "name " << nameAt(i) << '\n';
510  }
511  if (type() != AVRO_SYMBOLIC && leafAttributes_.hasAttribute) {
512  leafAt(i)->printBasicInfo(os);
513  }
514  }
515  if (isCompound(type())) {
516  os << "end " << type() << '\n';
517  }
518 }
519 
520 inline NodePtr resolveSymbol(const NodePtr &node) {
521  if (node->type() != AVRO_SYMBOLIC) {
522  throw Exception("Only symbolic nodes may be resolved");
523  }
524  std::shared_ptr<NodeSymbolic> symNode = std::static_pointer_cast<NodeSymbolic>(node);
525  return symNode->getNode();
526 }
527 
528 template<typename T>
529 inline std::string intToHex(T i) {
530  std::stringstream stream;
531  stream << "\\u"
532  << std::setfill('0') << std::setw(sizeof(T))
533  << std::hex << i;
534  return stream.str();
535 }
536 
537 } // namespace avro
538 
539 #endif
avro::AVRO_NULL
@ AVRO_NULL
Definition: Types.hh:40
avro::Node
Node is the building block for parse trees.
Definition: Node.hh:90
avro::NodeFixed
Definition: NodeImpl.hh:457
avro::AVRO_LONG
@ AVRO_LONG
Definition: Types.hh:36
avro::AVRO_ENUM
@ AVRO_ENUM
Definition: Types.hh:43
avro::NodeMap
Definition: NodeImpl.hh:364
avro::RESOLVE_MATCH
@ RESOLVE_MATCH
The schemas match at a cursory level.
Definition: SchemaResolution.hh:37
avro::AVRO_FLOAT
@ AVRO_FLOAT
Definition: Types.hh:37
avro::NodeImpl::leafAttributes_
LeavesConcept leafAttributes_
Doc used to compare schemas.
Definition: NodeImpl.hh:196
avro::AVRO_BOOL
@ AVRO_BOOL
Definition: Types.hh:39
avro::AVRO_STRING
@ AVRO_STRING
Definition: Types.hh:33
avro::Name
Definition: Node.hh:41
avro::NodeEnum
Definition: NodeImpl.hh:323
avro::AVRO_BYTES
@ AVRO_BYTES
Definition: Types.hh:34
avro::NodeRecord
Definition: NodeImpl.hh:281
avro::concepts::MultiAttribute
Definition: NodeConcepts.hh:121
avro::NodeSymbolic
Definition: NodeImpl.hh:242
avro::GenericDatum
Generic datum which can hold any Avro type.
Definition: GenericDatum.hh:61
avro::concepts::NoAttribute
Definition: NodeConcepts.hh:50
avro::NodePrimitive
Definition: NodeImpl.hh:227
avro::AVRO_INT
@ AVRO_INT
Definition: Types.hh:35
avro
A bunch of templates and specializations for encoding and decoding specific types.
Definition: AvroParse.hh:30
avro::NodeUnion
Definition: NodeImpl.hh:388
avro::RESOLVE_NO_MATCH
@ RESOLVE_NO_MATCH
The schemas definitely do not match.
Definition: SchemaResolution.hh:30
avro::AVRO_ARRAY
@ AVRO_ARRAY
Definition: Types.hh:44
avro::AVRO_DOUBLE
@ AVRO_DOUBLE
Definition: Types.hh:38
avro::isCompound
constexpr bool isCompound(Type t) noexcept
Returns true if and only if the given type is a non primitive valid type.
Definition: Types.hh:71
avro::AVRO_FIXED
@ AVRO_FIXED
Definition: Types.hh:47
avro::SchemaResolution
SchemaResolution
Definition: SchemaResolution.hh:26
avro::concepts::SingleAttribute< std::string >
avro::AVRO_RECORD
@ AVRO_RECORD
Definition: Types.hh:42
avro::NodeImpl
Implementation details for Node.
Definition: NodeImpl.hh:46
avro::concepts::NameIndexConcept< LeafNamesConcept >
avro::AVRO_SYMBOLIC
@ AVRO_SYMBOLIC
Definition: Types.hh:53
avro::NodeArray
Definition: NodeImpl.hh:347
avro::Type
Type
The "type" for the schema.
Definition: Types.hh:31
avro::AVRO_UNION
@ AVRO_UNION
Definition: Types.hh:46
avro::AVRO_MAP
@ AVRO_MAP
Definition: Types.hh:45
avro::Exception
Wrapper for std::runtime_error that provides convenience constructor for boost::format objects.
Definition: Exception.hh:31