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