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 <limits>
26 #include <set>
27 #include <iomanip>
28 #include <iostream>
29 #include <sstream>
30 #include <memory>
31 
32 #include "Node.hh"
33 #include "NodeConcepts.hh"
34 
35 namespace avro {
36 
39 
40 template
41 <
42  class NameConcept,
43  class LeavesConcept,
44  class LeafNamesConcept,
45  class SizeConcept
46 >
47 class NodeImpl : public Node
48 {
49 
50  protected:
51 
52  NodeImpl(Type type) :
53  Node(type),
54  nameAttribute_(),
55  docAttribute_(),
57  leafNameAttributes_(),
58  sizeAttribute_()
59  { }
60 
61  NodeImpl(Type type,
62  const NameConcept &name,
63  const LeavesConcept &leaves,
64  const LeafNamesConcept &leafNames,
65  const SizeConcept &size) :
66  Node(type),
67  nameAttribute_(name),
68  docAttribute_(),
69  leafAttributes_(leaves),
70  leafNameAttributes_(leafNames),
71  sizeAttribute_(size)
72  { }
73 
74  // Ctor with "doc"
75  NodeImpl(Type type,
76  const NameConcept &name,
78  const LeavesConcept &leaves,
79  const LeafNamesConcept &leafNames,
80  const SizeConcept &size) :
81  Node(type),
82  nameAttribute_(name),
83  docAttribute_(doc),
84  leafAttributes_(leaves),
85  leafNameAttributes_(leafNames),
86  sizeAttribute_(size)
87  {}
88 
89  void swap(NodeImpl& impl) {
90  std::swap(nameAttribute_, impl.nameAttribute_);
91  std::swap(docAttribute_, impl.docAttribute_);
92  std::swap(leafAttributes_, impl.leafAttributes_);
93  std::swap(leafNameAttributes_, impl.leafNameAttributes_);
94  std::swap(sizeAttribute_, impl.sizeAttribute_);
95  std::swap(nameIndex_, impl.nameIndex_);
96  }
97 
98  bool hasName() const {
99  // e.g.: true for single and multiattributes, false for noattributes.
100  return NameConcept::hasAttribute;
101  }
102 
103  void doSetName(const Name &name) {
104  nameAttribute_.add(name);
105  }
106 
107  const Name &name() const {
108  return nameAttribute_.get();
109  }
110 
111  void doSetDoc(const std::string &doc) {
112  docAttribute_.add(doc);
113  }
114 
115  const std::string &getDoc() const {
116  return docAttribute_.get();
117  }
118 
119  void doAddLeaf(const NodePtr &newLeaf) {
120  leafAttributes_.add(newLeaf);
121  }
122 
123  size_t leaves() const {
124  return leafAttributes_.size();
125  }
126 
127  const NodePtr &leafAt(int index) const {
128  return leafAttributes_.get(index);
129  }
130 
131  void doAddName(const std::string &name) {
132  if (! nameIndex_.add(name, leafNameAttributes_.size())) {
133  throw Exception(boost::format("Cannot add duplicate name: %1%") % name);
134  }
135  leafNameAttributes_.add(name);
136  }
137 
138  size_t names() const {
139  return leafNameAttributes_.size();
140  }
141 
142  const std::string &nameAt(int index) const {
143  return leafNameAttributes_.get(index);
144  }
145 
146  bool nameIndex(const std::string &name, size_t &index) const {
147  return nameIndex_.lookup(name, index);
148  }
149 
150  void doSetFixedSize(int size) {
151  sizeAttribute_.add(size);
152  }
153 
154  int fixedSize() const {
155  return sizeAttribute_.get();
156  }
157 
158  virtual bool isValid() const = 0;
159 
160  void printBasicInfo(std::ostream &os) const;
161 
162  void setLeafToSymbolic(int index, const NodePtr &node);
163 
164  SchemaResolution furtherResolution(const Node &reader) const {
166 
167  if (reader.type() == AVRO_SYMBOLIC) {
168 
169  // resolve the symbolic type, and check again
170  const NodePtr &node = reader.leafAt(0);
171  match = resolve(*node);
172  }
173  else if(reader.type() == AVRO_UNION) {
174 
175  // in this case, need to see if there is an exact match for the
176  // writer's type, or if not, the first one that can be promoted to a
177  // match
178 
179  for(size_t i= 0; i < reader.leaves(); ++i) {
180 
181  const NodePtr &node = reader.leafAt(i);
182  SchemaResolution thisMatch = resolve(*node);
183 
184  // if matched then the search is done
185  if(thisMatch == RESOLVE_MATCH) {
186  match = thisMatch;
187  break;
188  }
189 
190  // thisMatch is either no match, or promotable, this will set match to
191  // promotable if it hasn't been set already
192  if (match == RESOLVE_NO_MATCH) {
193  match = thisMatch;
194  }
195  }
196  }
197 
198  return match;
199  }
200 
201  NameConcept nameAttribute_;
202 
203  // Rem: NameConcept type is HasName (= SingleAttribute<Name>), we use std::string instead
206  LeavesConcept leafAttributes_;
207  LeafNamesConcept leafNameAttributes_;
208  SizeConcept sizeAttribute_;
210 };
211 
214 
216 
220 
223 
226 
229 
236 
237 class AVRO_DECL NodePrimitive : public NodeImplPrimitive
238 {
239  public:
240 
241  explicit NodePrimitive(Type type) :
242  NodeImplPrimitive(type)
243  { }
244 
245  SchemaResolution resolve(const Node &reader) const;
246 
247  void printJson(std::ostream &os, int depth) const;
248 
249  bool isValid() const {
250  return true;
251  }
252 
253  void printDefaultToJson(const GenericDatum& g, std::ostream &os, int depth) const;
254 };
255 
256 class AVRO_DECL NodeSymbolic : public NodeImplSymbolic
257 {
258  typedef std::weak_ptr<Node> NodeWeakPtr;
259 
260  public:
261 
262  NodeSymbolic() :
263  NodeImplSymbolic(AVRO_SYMBOLIC)
264  { }
265 
266  explicit NodeSymbolic(const HasName &name) :
267  NodeImplSymbolic(AVRO_SYMBOLIC, name, NoLeaves(), NoLeafNames(), NoSize())
268  { }
269 
270  NodeSymbolic(const HasName &name, const NodePtr n) :
271  NodeImplSymbolic(AVRO_SYMBOLIC, name, NoLeaves(), NoLeafNames(), NoSize()), actualNode_(n)
272  { }
273  SchemaResolution resolve(const Node &reader) const;
274 
275  void printJson(std::ostream &os, int depth) const;
276 
277  bool isValid() const {
278  return (nameAttribute_.size() == 1);
279  }
280 
281  void printDefaultToJson(const GenericDatum& g, std::ostream &os, int depth) const;
282 
283  bool isSet() const {
284  return (actualNode_.lock() != 0);
285  }
286 
287  NodePtr getNode() const {
288  NodePtr node = actualNode_.lock();
289  if(!node) {
290  throw Exception(boost::format("Could not follow symbol %1%") % name());
291  }
292  return node;
293  }
294 
295  void setNode(const NodePtr &node) {
296  actualNode_ = node;
297  }
298 
299  protected:
300 
301  NodeWeakPtr actualNode_;
302 
303 };
304 
305 class AVRO_DECL NodeRecord : public NodeImplRecord {
306  std::vector<GenericDatum> defaultValues;
307 public:
308  NodeRecord() : NodeImplRecord(AVRO_RECORD) { }
309  NodeRecord(const HasName &name, const MultiLeaves &fields,
310  const LeafNames &fieldsNames,
311  const std::vector<GenericDatum>& dv) :
312  NodeImplRecord(AVRO_RECORD, name, fields, fieldsNames, NoSize()),
313  defaultValues(dv) {
314  for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
315  if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
316  throw Exception(boost::format(
317  "Cannot add duplicate field: %1%") %
318  leafNameAttributes_.get(i));
319  }
320  }
321  }
322 
323  NodeRecord(const HasName &name, const HasDoc &doc, const MultiLeaves &fields,
324  const LeafNames &fieldsNames,
325  const std::vector<GenericDatum> &dv) :
326  NodeImplRecord(AVRO_RECORD, name, doc, fields, fieldsNames, NoSize()),
327  defaultValues(dv) {
328  for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
329  if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
330  throw Exception(boost::format(
331  "Cannot add duplicate field: %1%") %
332  leafNameAttributes_.get(i));
333  }
334  }
335  }
336 
337  void swap(NodeRecord& r) {
338  NodeImplRecord::swap(r);
339  defaultValues.swap(r.defaultValues);
340  }
341 
342  SchemaResolution resolve(const Node &reader) const;
343 
344  void printJson(std::ostream &os, int depth) const;
345 
346  bool isValid() const {
347  return ((nameAttribute_.size() == 1) &&
348  (leafAttributes_.size() == leafNameAttributes_.size()));
349  }
350 
351  const GenericDatum& defaultValueAt(int index) {
352  return defaultValues[index];
353  }
354 
355  void printDefaultToJson(const GenericDatum& g, std::ostream &os, int depth) const;
356 };
357 
358 class AVRO_DECL NodeEnum : public NodeImplEnum
359 {
360  public:
361 
362  NodeEnum() :
363  NodeImplEnum(AVRO_ENUM)
364  { }
365 
366  NodeEnum(const HasName &name, const LeafNames &symbols) :
367  NodeImplEnum(AVRO_ENUM, name, NoLeaves(), symbols, NoSize())
368  {
369  for(size_t i=0; i < leafNameAttributes_.size(); ++i) {
370  if(!nameIndex_.add(leafNameAttributes_.get(i), i)) {
371  throw Exception(boost::format("Cannot add duplicate enum: %1%") % leafNameAttributes_.get(i));
372  }
373  }
374  }
375 
376  SchemaResolution resolve(const Node &reader) const;
377 
378  void printJson(std::ostream &os, int depth) const;
379 
380  bool isValid() const {
381  return (
382  (nameAttribute_.size() == 1) &&
383  (leafNameAttributes_.size() > 0)
384  );
385  }
386 
387  void printDefaultToJson(const GenericDatum& g, std::ostream &os, int depth) const;
388 };
389 
390 class AVRO_DECL NodeArray : public NodeImplArray
391 {
392  public:
393 
394  NodeArray() :
395  NodeImplArray(AVRO_ARRAY)
396  { }
397 
398  explicit NodeArray(const SingleLeaf &items) :
399  NodeImplArray(AVRO_ARRAY, NoName(), items, NoLeafNames(), NoSize())
400  { }
401 
402  SchemaResolution resolve(const Node &reader) const;
403 
404  void printJson(std::ostream &os, int depth) const;
405 
406  bool isValid() const {
407  return (leafAttributes_.size() == 1);
408  }
409 
410  void printDefaultToJson(const GenericDatum& g, std::ostream &os, int depth) const;
411 };
412 
413 class AVRO_DECL NodeMap : public NodeImplMap
414 {
415  public:
416 
417  NodeMap() :
418  NodeImplMap(AVRO_MAP)
419  {
420  NodePtr key(new NodePrimitive(AVRO_STRING));
421  doAddLeaf(key);
422  }
423 
424  explicit NodeMap(const SingleLeaf &values) :
425  NodeImplMap(AVRO_MAP, NoName(), values, NoLeafNames(), NoSize())
426  {
427  // need to add the key for the map too
428  NodePtr key(new NodePrimitive(AVRO_STRING));
429  doAddLeaf(key);
430 
431  // key goes before value
432  std::swap(leafAttributes_.get(0), leafAttributes_.get(1));
433  }
434 
435  SchemaResolution resolve(const Node &reader) const;
436 
437  void printJson(std::ostream &os, int depth) const;
438 
439  bool isValid() const {
440  return (leafAttributes_.size() == 2);
441  }
442 
443  void printDefaultToJson(const GenericDatum& g, std::ostream &os, int depth) const;
444 };
445 
446 class AVRO_DECL NodeUnion : public NodeImplUnion
447 {
448  public:
449 
450  NodeUnion() :
451  NodeImplUnion(AVRO_UNION)
452  { }
453 
454  explicit NodeUnion(const MultiLeaves &types) :
455  NodeImplUnion(AVRO_UNION, NoName(), types, NoLeafNames(), NoSize())
456  { }
457 
458  SchemaResolution resolve(const Node &reader) const;
459 
460  void printJson(std::ostream &os, int depth) const;
461 
462  bool isValid() const {
463  std::set<std::string> seen;
464  if (leafAttributes_.size() >= 1) {
465  for (size_t i = 0; i < leafAttributes_.size(); ++i) {
466  std::string name;
467  const NodePtr& n = leafAttributes_.get(i);
468  switch (n->type()) {
469  case AVRO_STRING:
470  name = "string";
471  break;
472  case AVRO_BYTES:
473  name = "bytes";
474  break;
475  case AVRO_INT:
476  name = "int";
477  break;
478  case AVRO_LONG:
479  name = "long";
480  break;
481  case AVRO_FLOAT:
482  name = "float";
483  break;
484  case AVRO_DOUBLE:
485  name = "double";
486  break;
487  case AVRO_BOOL:
488  name = "bool";
489  break;
490  case AVRO_NULL:
491  name = "null";
492  break;
493  case AVRO_ARRAY:
494  name = "array";
495  break;
496  case AVRO_MAP:
497  name = "map";
498  break;
499  case AVRO_RECORD:
500  case AVRO_ENUM:
501  case AVRO_UNION:
502  case AVRO_FIXED:
503  case AVRO_SYMBOLIC:
504  name = n->name().fullname();
505  break;
506  default:
507  return false;
508  }
509  if (seen.find(name) != seen.end()) {
510  return false;
511  }
512  seen.insert(name);
513  }
514  return true;
515  }
516  return false;
517  }
518 
519  void printDefaultToJson(const GenericDatum& g, std::ostream &os, int depth) const;
520 };
521 
522 class AVRO_DECL NodeFixed : public NodeImplFixed
523 {
524  public:
525 
526  NodeFixed() :
527  NodeImplFixed(AVRO_FIXED)
528  { }
529 
530  NodeFixed(const HasName &name, const HasSize &size) :
531  NodeImplFixed(AVRO_FIXED, name, NoLeaves(), NoLeafNames(), size)
532  { }
533 
534  SchemaResolution resolve(const Node &reader) const;
535 
536  void printJson(std::ostream &os, int depth) const;
537 
538  bool isValid() const {
539  return (
540  (nameAttribute_.size() == 1) &&
541  (sizeAttribute_.size() == 1)
542  );
543  }
544 
545  void printDefaultToJson(const GenericDatum& g, std::ostream &os, int depth) const;
546 };
547 
548 template < class A, class B, class C, class D >
549 inline void
550 NodeImpl<A,B,C,D>::setLeafToSymbolic(int index, const NodePtr &node)
551 {
552  if(!B::hasAttribute) {
553  throw Exception("Cannot change leaf node for nonexistent leaf");
554  }
555 
556  NodePtr &replaceNode = const_cast<NodePtr &>(leafAttributes_.get(index));
557  if(replaceNode->name() != node->name()) {
558  throw Exception("Symbolic name does not match the name of the schema it references");
559  }
560 
561  NodePtr symbol(new NodeSymbolic);
562  NodeSymbolic *ptr = static_cast<NodeSymbolic *> (symbol.get());
563 
564  ptr->setName(node->name());
565  ptr->setNode(node);
566  replaceNode.swap(symbol);
567 }
568 
569 template < class A, class B, class C, class D >
570 inline void
571 NodeImpl<A,B,C,D>::printBasicInfo(std::ostream &os) const
572 {
573  os << type();
574  if(hasName()) {
575  os << ' ' << nameAttribute_.get();
576  }
577 
578  if(D::hasAttribute) {
579  os << " " << sizeAttribute_.get();
580  }
581  os << '\n';
582  int count = leaves();
583  count = count ? count : names();
584  for(int i= 0; i < count; ++i) {
585  if( C::hasAttribute ) {
586  os << "name " << nameAt(i) << '\n';
587  }
588  if( type() != AVRO_SYMBOLIC && leafAttributes_.hasAttribute) {
589  leafAt(i)->printBasicInfo(os);
590  }
591  }
592  if(isCompound(type())) {
593  os << "end " << type() << '\n';
594  }
595 }
596 
597 
598 inline NodePtr resolveSymbol(const NodePtr &node)
599 {
600  if(node->type() != AVRO_SYMBOLIC) {
601  throw Exception("Only symbolic nodes may be resolved");
602  }
603  std::shared_ptr<NodeSymbolic> symNode = std::static_pointer_cast<NodeSymbolic>(node);
604  return symNode->getNode();
605 }
606 
607 template< typename T >
608 inline std::string intToHex(T i)
609 {
610  std::stringstream stream;
611  stream << "\\u"
612  << std::setfill('0') << std::setw(sizeof(T))
613  << std::hex << i;
614  return stream.str();
615 }
616 
617 } // namespace avro
618 
619 #endif
Node is the building block for parse trees.
Definition: Node.hh:89
Definition: NodeImpl.hh:358
Definition: Types.hh:37
Definition: Types.hh:45
The schemas match at a cursory level.
Definition: SchemaResolution.hh:38
Definition: Node.hh:40
Definition: Types.hh:40
Type
The "type" for the schema.
Definition: Types.hh:31
A bunch of templates and specializations for encoding and decoding specific types.
Definition: AvroParse.hh:30
Definition: NodeImpl.hh:256
Definition: Types.hh:43
Definition: NodeImpl.hh:390
Definition: Types.hh:39
SchemaResolution
Definition: SchemaResolution.hh:27
Definition: Types.hh:33
Definition: NodeImpl.hh:237
Definition: NodeImpl.hh:413
Definition: NodeConcepts.hh:132
Definition: Types.hh:44
Definition: Types.hh:35
Definition: Types.hh:47
Definition: Types.hh:34
LeavesConcept leafAttributes_
Doc used to compare schemas.
Definition: NodeImpl.hh:206
The schemas definitely do not match.
Definition: SchemaResolution.hh:31
Definition: Types.hh:38
Wrapper for std::runtime_error that provides convenience constructor for boost::format objects...
Definition: Exception.hh:31
Definition: NodeConcepts.hh:51
Definition: NodeImpl.hh:305
Implementation details for Node.
Definition: NodeImpl.hh:47
Definition: Types.hh:42
Definition: Types.hh:46
Definition: NodeImpl.hh:446
bool isCompound(Type t)
Returns true if and only if the given type is a non primitive valid type.
Definition: Types.hh:72
Definition: Types.hh:36
Definition: Types.hh:53
Generic datum which can hold any Avro type.
Definition: GenericDatum.hh:61
Definition: NodeImpl.hh:522