Avro C++
NodeImpl.hh
00001 /*
00002  * Licensed to the Apache Software Foundation (ASF) under one
00003  * or more contributor license agreements.  See the NOTICE file
00004  * distributed with this work for additional information
00005  * regarding copyright ownership.  The ASF licenses this file
00006  * to you under the Apache License, Version 2.0 (the
00007  * "License"); you may not use this file except in compliance
00008  * with the License.  You may obtain a copy of the License at
00009  *
00010  *     http://www.apache.org/licenses/LICENSE-2.0
00011  *
00012  * Unless required by applicable law or agreed to in writing, software
00013  * distributed under the License is distributed on an "AS IS" BASIS,
00014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015  * See the License for the specific language governing permissions and
00016  * limitations under the License.
00017  */
00018 
00019 #ifndef avro_NodeImpl_hh__
00020 #define avro_NodeImpl_hh__
00021 
00022 #include "Config.hh"
00023 #include "GenericDatum.hh"
00024 
00025 #include <limits>
00026 #include <set>
00027 #include <boost/weak_ptr.hpp>
00028 
00029 #include "Node.hh"
00030 #include "NodeConcepts.hh"
00031 
00032 namespace avro {
00033 
00036 
00037 template 
00038 < 
00039     class NameConcept,
00040     class LeavesConcept,
00041     class LeafNamesConcept,
00042     class SizeConcept
00043 >
00044 class NodeImpl : public Node
00045 {
00046 
00047   protected:
00048 
00049     NodeImpl(Type type) :
00050         Node(type),
00051         nameAttribute_(),
00052         leafAttributes_(),
00053         leafNameAttributes_(),
00054         sizeAttribute_()
00055     { }
00056 
00057     NodeImpl(Type type, 
00058              const NameConcept &name, 
00059              const LeavesConcept &leaves, 
00060              const LeafNamesConcept &leafNames,
00061              const SizeConcept &size) :
00062         Node(type),
00063         nameAttribute_(name),
00064         leafAttributes_(leaves),
00065         leafNameAttributes_(leafNames),
00066         sizeAttribute_(size)
00067     { }
00068 
00069     void swap(NodeImpl& impl) {
00070         std::swap(nameAttribute_, impl.nameAttribute_);
00071         std::swap(leafAttributes_, impl.leafAttributes_);
00072         std::swap(leafNameAttributes_, impl.leafNameAttributes_);
00073         std::swap(sizeAttribute_, impl.sizeAttribute_);
00074         std::swap(nameIndex_, impl.nameIndex_);
00075     }
00076 
00077     bool hasName() const {
00078         return NameConcept::hasAttribute;
00079     }
00080 
00081     void doSetName(const Name &name) {
00082         nameAttribute_.add(name);
00083     }
00084     
00085     const Name &name() const {
00086         return nameAttribute_.get();
00087     }
00088 
00089     void doAddLeaf(const NodePtr &newLeaf) { 
00090         leafAttributes_.add(newLeaf);
00091     }
00092 
00093     size_t leaves() const {
00094         return leafAttributes_.size();
00095     }
00096 
00097     const NodePtr &leafAt(int index) const { 
00098         return leafAttributes_.get(index);
00099     }
00100 
00101     void doAddName(const std::string &name) { 
00102         if (! nameIndex_.add(name, leafNameAttributes_.size())) {
00103             throw Exception(boost::format("Cannot add duplicate name: %1%") % name);
00104         }
00105         leafNameAttributes_.add(name);
00106     }
00107 
00108     size_t names() const {
00109         return leafNameAttributes_.size();
00110     }
00111 
00112     const std::string &nameAt(int index) const { 
00113         return leafNameAttributes_.get(index);
00114     }
00115 
00116     bool nameIndex(const std::string &name, size_t &index) const {
00117         return nameIndex_.lookup(name, index);
00118     }
00119 
00120     void doSetFixedSize(int size) {
00121         sizeAttribute_.add(size);
00122     }
00123 
00124     int fixedSize() const {
00125         return sizeAttribute_.get();
00126     }
00127 
00128     virtual bool isValid() const = 0;
00129 
00130     void printBasicInfo(std::ostream &os) const;
00131 
00132     void setLeafToSymbolic(int index, const NodePtr &node);
00133    
00134     SchemaResolution furtherResolution(const Node &reader) const {
00135         SchemaResolution match = RESOLVE_NO_MATCH;
00136 
00137         if (reader.type() == AVRO_SYMBOLIC) {
00138     
00139             // resolve the symbolic type, and check again
00140             const NodePtr &node = reader.leafAt(0);
00141             match = resolve(*node);
00142         }
00143         else if(reader.type() == AVRO_UNION) {
00144 
00145             // in this case, need to see if there is an exact match for the
00146             // writer's type, or if not, the first one that can be promoted to a
00147             // match
00148         
00149             for(size_t i= 0; i < reader.leaves(); ++i)  {
00150 
00151                 const NodePtr &node = reader.leafAt(i);
00152                 SchemaResolution thisMatch = resolve(*node);
00153 
00154                 // if matched then the search is done
00155                 if(thisMatch == RESOLVE_MATCH) {
00156                     match = thisMatch;
00157                     break;
00158                 }
00159 
00160                 // thisMatch is either no match, or promotable, this will set match to 
00161                 // promotable if it hasn't been set already
00162                 if (match == RESOLVE_NO_MATCH) {
00163                     match = thisMatch;
00164                 }
00165             }
00166         }
00167 
00168         return match;
00169     }
00170 
00171     NameConcept nameAttribute_;
00172     LeavesConcept leafAttributes_;
00173     LeafNamesConcept leafNameAttributes_;
00174     SizeConcept sizeAttribute_;
00175     concepts::NameIndexConcept<LeafNamesConcept> nameIndex_;
00176 };
00177 
00178 typedef concepts::NoAttribute<Name>     NoName;
00179 typedef concepts::SingleAttribute<Name> HasName;
00180 
00181 typedef concepts::NoAttribute<NodePtr>      NoLeaves;
00182 typedef concepts::SingleAttribute<NodePtr>  SingleLeaf;
00183 typedef concepts::MultiAttribute<NodePtr>   MultiLeaves;
00184 
00185 typedef concepts::NoAttribute<std::string>     NoLeafNames;
00186 typedef concepts::MultiAttribute<std::string>  LeafNames;
00187 
00188 typedef concepts::NoAttribute<int>     NoSize;
00189 typedef concepts::SingleAttribute<int> HasSize;
00190 
00191 typedef NodeImpl< NoName,  NoLeaves,    NoLeafNames,  NoSize  > NodeImplPrimitive;
00192 typedef NodeImpl< HasName, NoLeaves,    NoLeafNames,  NoSize  > NodeImplSymbolic;
00193 
00194 typedef NodeImpl< HasName, MultiLeaves, LeafNames,    NoSize  > NodeImplRecord;
00195 typedef NodeImpl< HasName, NoLeaves,    LeafNames,    NoSize  > NodeImplEnum;
00196 typedef NodeImpl< NoName,  SingleLeaf,  NoLeafNames,  NoSize  > NodeImplArray;
00197 typedef NodeImpl< NoName,  MultiLeaves, NoLeafNames,  NoSize  > NodeImplMap;
00198 typedef NodeImpl< NoName,  MultiLeaves, NoLeafNames,  NoSize  > NodeImplUnion;
00199 typedef NodeImpl< HasName, NoLeaves,    NoLeafNames,  HasSize > NodeImplFixed;
00200 
00201 class AVRO_DECL NodePrimitive : public NodeImplPrimitive
00202 {
00203   public:
00204 
00205     explicit NodePrimitive(Type type) :
00206         NodeImplPrimitive(type)
00207     { }
00208 
00209     SchemaResolution resolve(const Node &reader)  const;
00210 
00211     void printJson(std::ostream &os, int depth) const;
00212 
00213     bool isValid() const {
00214         return true;
00215     }
00216 };
00217 
00218 class AVRO_DECL NodeSymbolic : public NodeImplSymbolic
00219 {
00220     typedef boost::weak_ptr<Node> NodeWeakPtr;
00221 
00222   public:
00223 
00224     NodeSymbolic() :
00225         NodeImplSymbolic(AVRO_SYMBOLIC)
00226     { }
00227 
00228     explicit NodeSymbolic(const HasName &name) :
00229         NodeImplSymbolic(AVRO_SYMBOLIC, name, NoLeaves(), NoLeafNames(), NoSize())
00230     { }
00231 
00232     NodeSymbolic(const HasName &name, const NodePtr n) :
00233         NodeImplSymbolic(AVRO_SYMBOLIC, name, NoLeaves(), NoLeafNames(), NoSize()), actualNode_(n)
00234     { }
00235     SchemaResolution resolve(const Node &reader)  const;
00236 
00237     void printJson(std::ostream &os, int depth) const;
00238 
00239     bool isValid() const {
00240         return (nameAttribute_.size() == 1);
00241     }
00242 
00243     bool isSet() const {
00244          return (actualNode_.lock() != 0);
00245     }
00246 
00247     NodePtr getNode() const {
00248         NodePtr node = actualNode_.lock();
00249         if(!node) {
00250             throw Exception(boost::format("Could not follow symbol %1%") % name());
00251         }
00252         return node;
00253     }
00254 
00255     void setNode(const NodePtr &node) {
00256         actualNode_ = node;
00257     }
00258 
00259   protected:
00260 
00261     NodeWeakPtr actualNode_;
00262 
00263 };
00264 
00265 class AVRO_DECL NodeRecord : public NodeImplRecord {
00266     std::vector<GenericDatum> defaultValues;
00267 public:
00268     NodeRecord() : NodeImplRecord(AVRO_RECORD) { } 
00269     NodeRecord(const HasName &name, const MultiLeaves &fields,
00270         const LeafNames &fieldsNames,
00271         const std::vector<GenericDatum>& dv) :
00272         NodeImplRecord(AVRO_RECORD, name, fields, fieldsNames, NoSize()),
00273         defaultValues(dv) { 
00274         for (size_t i = 0; i < leafNameAttributes_.size(); ++i) {
00275             if (!nameIndex_.add(leafNameAttributes_.get(i), i)) {
00276                 throw Exception(boost::format(
00277                     "Cannot add duplicate name: %1%") %
00278                     leafNameAttributes_.get(i));
00279             }
00280         }
00281     }
00282 
00283     void swap(NodeRecord& r) {
00284         NodeImplRecord::swap(r);
00285         defaultValues.swap(r.defaultValues);
00286     }
00287 
00288     SchemaResolution resolve(const Node &reader)  const;
00289 
00290     void printJson(std::ostream &os, int depth) const;
00291 
00292     bool isValid() const {
00293         return ((nameAttribute_.size() == 1) && 
00294             (leafAttributes_.size() == leafNameAttributes_.size()));
00295     }
00296 
00297     const GenericDatum& defaultValueAt(int index) {
00298         return defaultValues[index];
00299     }
00300 };
00301 
00302 class AVRO_DECL NodeEnum : public NodeImplEnum
00303 {
00304   public:
00305 
00306     NodeEnum() :
00307         NodeImplEnum(AVRO_ENUM) 
00308     { }
00309 
00310     NodeEnum(const HasName &name, const LeafNames &symbols) :
00311         NodeImplEnum(AVRO_ENUM, name, NoLeaves(), symbols, NoSize())
00312     { 
00313         for(size_t i=0; i < leafNameAttributes_.size(); ++i) {
00314             if(!nameIndex_.add(leafNameAttributes_.get(i), i)) {
00315                  throw Exception(boost::format("Cannot add duplicate name: %1%") % leafNameAttributes_.get(i));
00316             }
00317         }
00318     }
00319         
00320     SchemaResolution resolve(const Node &reader)  const;
00321 
00322     void printJson(std::ostream &os, int depth) const;
00323 
00324     bool isValid() const {
00325         return (
00326                 (nameAttribute_.size() == 1) && 
00327                 (leafNameAttributes_.size() > 0) 
00328                );
00329     }
00330 };
00331 
00332 class AVRO_DECL NodeArray : public NodeImplArray
00333 {
00334   public:
00335 
00336     NodeArray() :
00337         NodeImplArray(AVRO_ARRAY)
00338     { }
00339 
00340     explicit NodeArray(const SingleLeaf &items) :
00341         NodeImplArray(AVRO_ARRAY, NoName(), items, NoLeafNames(), NoSize())
00342     { }
00343 
00344     SchemaResolution resolve(const Node &reader)  const;
00345 
00346     void printJson(std::ostream &os, int depth) const;
00347 
00348     bool isValid() const {
00349         return (leafAttributes_.size() == 1);
00350     }
00351 };
00352 
00353 class AVRO_DECL NodeMap : public NodeImplMap
00354 {
00355   public:
00356 
00357     NodeMap() :
00358         NodeImplMap(AVRO_MAP)
00359     { 
00360          NodePtr key(new NodePrimitive(AVRO_STRING));
00361          doAddLeaf(key);
00362     }
00363 
00364     explicit NodeMap(const SingleLeaf &values) :
00365         NodeImplMap(AVRO_MAP, NoName(), values, NoLeafNames(), NoSize())
00366     { 
00367         // need to add the key for the map too
00368         NodePtr key(new NodePrimitive(AVRO_STRING));
00369         doAddLeaf(key);
00370 
00371         // key goes before value
00372         std::swap(leafAttributes_.get(0), leafAttributes_.get(1));
00373     }
00374 
00375     SchemaResolution resolve(const Node &reader)  const;
00376 
00377     void printJson(std::ostream &os, int depth) const;
00378 
00379     bool isValid() const {
00380         return (leafAttributes_.size() == 2);
00381     }
00382 };
00383 
00384 class AVRO_DECL NodeUnion : public NodeImplUnion
00385 {
00386   public:
00387 
00388     NodeUnion() :
00389         NodeImplUnion(AVRO_UNION)
00390     { }
00391 
00392     explicit NodeUnion(const MultiLeaves &types) :
00393         NodeImplUnion(AVRO_UNION, NoName(), types, NoLeafNames(), NoSize())
00394     { }
00395 
00396     SchemaResolution resolve(const Node &reader)  const;
00397 
00398     void printJson(std::ostream &os, int depth) const;
00399 
00400     bool isValid() const {
00401         std::set<std::string> seen;
00402         if (leafAttributes_.size() >= 1) {
00403             for (size_t i = 0; i < leafAttributes_.size(); ++i) {
00404                 std::string name;
00405                 const NodePtr& n = leafAttributes_.get(i);
00406                 switch (n->type()) {
00407                 case AVRO_STRING:
00408                     name = "string";
00409                     break;
00410                 case AVRO_BYTES:
00411                     name = "bytes";
00412                     break;
00413                 case AVRO_INT:
00414                     name = "int";
00415                     break;
00416                 case AVRO_LONG:
00417                     name = "long";
00418                     break;
00419                 case AVRO_FLOAT:
00420                     name = "float";
00421                     break;
00422                 case AVRO_DOUBLE:
00423                     name = "double";
00424                     break;
00425                 case AVRO_BOOL:
00426                     name = "bool";
00427                     break;
00428                 case AVRO_NULL:
00429                     name = "null";
00430                     break;
00431                 case AVRO_ARRAY:
00432                     name = "array";
00433                     break;
00434                 case AVRO_MAP:
00435                     name = "map";
00436                     break;
00437                 case AVRO_RECORD:
00438                 case AVRO_ENUM:
00439                 case AVRO_UNION:
00440                 case AVRO_FIXED:
00441                 case AVRO_SYMBOLIC:
00442                     name = n->name().fullname();
00443                     break;
00444                 default:
00445                     return false;
00446                 }
00447                 if (seen.find(name) != seen.end()) {
00448                     return false;
00449                 }
00450                 seen.insert(name);
00451             }
00452             return true;
00453         }
00454         return false;
00455     }
00456 };
00457 
00458 class AVRO_DECL NodeFixed : public NodeImplFixed
00459 {
00460   public:
00461 
00462     NodeFixed() :
00463         NodeImplFixed(AVRO_FIXED)
00464     { }
00465 
00466     NodeFixed(const HasName &name, const HasSize &size) :
00467         NodeImplFixed(AVRO_FIXED, name, NoLeaves(), NoLeafNames(), size)
00468     { }
00469 
00470     SchemaResolution resolve(const Node &reader)  const;
00471 
00472     void printJson(std::ostream &os, int depth) const;
00473 
00474     bool isValid() const {
00475         return (
00476                 (nameAttribute_.size() == 1) && 
00477                 (sizeAttribute_.size() == 1) 
00478                );
00479     }
00480 };
00481 
00482 template < class A, class B, class C, class D >
00483 inline void 
00484 NodeImpl<A,B,C,D>::setLeafToSymbolic(int index, const NodePtr &node)
00485 {
00486     if(!B::hasAttribute) {
00487         throw Exception("Cannot change leaf node for nonexistent leaf");
00488     } 
00489 
00490     NodePtr &replaceNode = const_cast<NodePtr &>(leafAttributes_.get(index));
00491     if(replaceNode->name() != node->name()) {
00492         throw Exception("Symbolic name does not match the name of the schema it references");
00493     }
00494 
00495     NodePtr symbol(new NodeSymbolic);
00496     NodeSymbolic *ptr = static_cast<NodeSymbolic *> (symbol.get());
00497 
00498     ptr->setName(node->name());
00499     ptr->setNode(node);
00500     replaceNode.swap(symbol);
00501 }
00502 
00503 template < class A, class B, class C, class D >
00504 inline void 
00505 NodeImpl<A,B,C,D>::printBasicInfo(std::ostream &os) const
00506 {
00507     os << type();
00508     if(hasName()) {
00509         os << ' ' << nameAttribute_.get();
00510     }
00511 
00512     if(D::hasAttribute) {
00513         os << " " << sizeAttribute_.get();
00514     }
00515     os << '\n';
00516     int count = leaves();
00517     count = count ? count : names();
00518     for(int i= 0; i < count; ++i) {
00519         if( C::hasAttribute ) {
00520             os << "name " << nameAt(i) << '\n';
00521         }
00522         if( type() != AVRO_SYMBOLIC && leafAttributes_.hasAttribute) {
00523             leafAt(i)->printBasicInfo(os);
00524         }
00525     }
00526     if(isCompound(type())) {
00527         os << "end " << type() << '\n';
00528     }
00529 }
00530 
00531 
00532 inline NodePtr resolveSymbol(const NodePtr &node) 
00533 {
00534     if(node->type() != AVRO_SYMBOLIC) {
00535         throw Exception("Only symbolic nodes may be resolved");
00536     }
00537     boost::shared_ptr<NodeSymbolic> symNode = boost::static_pointer_cast<NodeSymbolic>(node);
00538     return symNode->getNode();
00539 }
00540 
00541 } // namespace avro
00542 
00543 #endif