expat_serializer.hpp

Go to the documentation of this file.
00001 #ifndef s11n_EXPAT_SERIALIZER_HPP_INCLUDED
00002 #define s11n_EXPAT_SERIALIZER_HPP_INCLUDED 1
00003 
00004 #include <s11n.net/s11n/io/strtool.hpp> // translate()
00005 #include <s11n.net/s11n/s11n_debuggering_macros.hpp> // COUT/CERR
00006 
00007 #include <s11n.net/s11n/io/data_node_format.hpp>
00008 
00009 #include <s11n.net/s11n/traits.hpp> // node_traits
00010 
00011 #include <expat.h>
00012 
00013 
00014 
00015 #define MAGIC_COOKIE_EXPAT_XML "<!DOCTYPE s11n::io::expat_serializer>"
00016 
00017 #include <stdexcept>
00018 #include <sstream>
00019 
00020 #define EXPATDEBUG if(0) CERR
00021 #define EXPAT_CLASS_ATTRIBUTE "class"
00022 namespace s11n {
00023 
00024         namespace io {
00025 
00026                 namespace sharing {
00027                         /**
00028                            Sharing context used by expat_serializer.
00029                          */
00030                         struct expat_sharing_context {};
00031 
00032                 }
00033                 /** convenience typedef */
00034                 typedef std::map<std::string,std::string> entity_translation_map;
00035 
00036 
00037                 /**
00038                    The entity translations map used by expat_serializer.
00039                  */
00040                 entity_translation_map & expat_serializer_translations();
00041                 /**
00042                    expat_serializer is an XML-based Serializer, using
00043                    libexpat to read it's data.
00044                 */
00045                 template <typename NodeType>
00046                 class expat_serializer : public data_node_serializer<NodeType>
00047                 {
00048                 public:
00049                         typedef NodeType node_type;
00050 
00051             /**
00052                Performs a "fast" check for XML key validity
00053                on s: if the first char is alpha or underscore,
00054                the function returns true, else it returns false.
00055             */
00056             static bool is_valid_xml_key( const std::string & s )
00057             {
00058                 return (
00059                     (!s.empty())
00060                     &&
00061                     (
00062                      std::isalpha(s[0])
00063                      ||
00064                      ('_' == s[0])
00065                      )
00066                     );
00067             }
00068 
00069                         typedef expat_serializer<node_type> this_type; // convenience typedef
00070 
00071                         expat_serializer() : m_depth(0)
00072                         {
00073                                 this->magic_cookie( MAGIC_COOKIE_EXPAT_XML );
00074                         }
00075 
00076                         virtual ~expat_serializer() {}
00077 
00078                         /**
00079                            Overridden to parse src using this object's grammar rules.
00080                         */
00081                         virtual node_type * deserialize( std::istream & src )
00082                         {
00083                                 return this->expat_parse_stream( src );
00084                         }
00085 
00086 
00087                         /**
00088                            Writes src out to dest.
00089                         */
00090                         virtual bool serialize( const node_type & src, std::ostream & dest )
00091             {
00092                 try
00093                 {
00094                     return this->serialize_impl( src, dest );
00095                 }
00096                 catch(...)
00097                 {
00098                     this->m_depth = 0;
00099                     throw;
00100                 }
00101                 return false; // can't get this far.
00102             }
00103 
00104         private:
00105 
00106                         /**
00107                            Writes src out to dest.
00108                         */
00109                         bool serialize_impl( const node_type & src, std::ostream & dest )
00110                         {
00111                                 typedef ::s11n::node_traits<node_type> NT;
00112 
00113 // INDENT() is a helper macro for some serializers.
00114 #define INDENT(LEVEL,ECHO) indent = ""; for( size_t i = 0; i < depth + LEVEL; i++ ) { indent += '\t'; if(ECHO) dest << '\t'; }
00115 
00116                                 //std::ostringstream buff; // we buffer so we can handle self-closing nodes
00117                                 size_t depth = this->m_depth++;
00118                                 if ( 0 == depth )
00119                                 {
00120                                         dest << this->magic_cookie() << '\n';
00121                                 }
00122 
00123 
00124                                 std::string nname = NT::name(src);
00125                 if( ! is_valid_xml_key( nname ) )
00126                 {
00127                     throw s11n::io_exception( "%s:%d: expat_serializer::serialize(): node name '%s' is not a valid XML tag name",
00128                                   __FILE__, __LINE__, nname.c_str() );
00129                 }
00130 
00131                                 std::string impl = NT::class_name(src);
00132                                 ::s11n::io::strtool::translate_entities( impl, expat_serializer_translations(), false );
00133                                 std::string indent;
00134                                 dest << "<" << nname << " "<<EXPAT_CLASS_ATTRIBUTE<<"=\""<< impl <<"\"";
00135                                 bool closed = false;
00136                                 typedef typename NT::property_map_type PMT;
00137                 typedef typename PMT::const_iterator CHIT;
00138                 CHIT cit, cet;
00139                 cit = NT::properties(src).begin();
00140                 cet = NT::properties(src).end();
00141                                 std::string propval;
00142                                 std::string key;
00143 
00144                                 if( cet != cit )
00145                                 { // got properties?
00146                                         closed = true;
00147                                         dest << ">\n";
00148                                         INDENT(1,0);
00149                                         for ( ; cet != cit; ++cit )
00150                                         {
00151                                                 key = ( *cit ).first;
00152                         if( ! is_valid_xml_key( key ) )
00153                         {
00154                             throw s11n::io_exception( "%s:%d: expat_serializer::serialize(): key '%s' is not a valid XML tag name.",
00155                                           __FILE__, __LINE__, key.c_str() );
00156                         }
00157                                                 propval = ( *cit ).second;
00158                                                 dest << indent << "<" << key;
00159                                                 if( propval.empty() )
00160                                                 {
00161                                                         dest << "/>";
00162                                                 }
00163                                                 else
00164                                                 {
00165                                                         dest << ">";
00166                                                         ::s11n::io::strtool::translate_entities( propval, expat_serializer_translations(), false );
00167                                                         dest << propval;
00168                                                         dest << "</" << key << ">";
00169                                                 }
00170                                                 dest << "\n";
00171                                         }
00172                                 }
00173 
00174                 
00175                 typedef typename NT::child_list_type CHLT;
00176                                 typename CHLT::const_iterator chit = NT::children(src).begin(),
00177                                         chet = NT::children(src).end();
00178                                 if( chet != chit )
00179                                 { // got kids?
00180                                         if( !closed )
00181                                         { // close node opener
00182                                                 dest << ">\n";
00183                                                 closed = true;
00184                                         }
00185                                         INDENT(1,0);
00186                     for( ; chet != chit; ++chit )
00187                     {
00188                         dest << indent;
00189                         this->serialize_impl( *(*chit), dest );
00190                     }
00191                                 }
00192 
00193                                 if( closed )
00194                                 {
00195                                         INDENT(0,1);
00196                                         dest << "</" << nname << ">";
00197                                 }
00198                                 else // self-close node
00199                                 {
00200                                         dest << "/>";
00201                                 }
00202 
00203                 dest << '\n';
00204                                 if( 0 == depth )
00205                                 {
00206                                         dest.flush();
00207                                         // if we don't do this then the client is possibly forced to flush() the stream :/
00208                                 }
00209                                 --this->m_depth;
00210                                 return true;
00211 #undef INDENT
00212                         }
00213 
00214                         static void XMLCALL start_node( void *, const char * name, const char ** attr )
00215                         {
00216                                 m_cbuf = "";
00217                                 if( attr[0] )
00218                                 { // object node
00219                                         std::string clname = "WTF?";
00220                                         const std::string classnameattr = std::string(EXPAT_CLASS_ATTRIBUTE);
00221                                         for( int i = 0; attr[i]; i += 2 )
00222                                         {
00223                                                 if( attr[i] == classnameattr )
00224                                                 {
00225                                                         clname = attr[i+1];
00226                                                         break;
00227                                                 }
00228                                         }
00229                                         EXPATDEBUG << "Opening object node["<<clname<<","<<name<<"]\n";
00230                                         m_builder.open_node( clname, name );
00231                                 }
00232                                 else // property node
00233                                 {
00234                                         m_name = name;
00235                                         EXPATDEBUG << "Opening property node["<<m_name<<"]\n";
00236                                 }
00237                         }
00238 
00239                         /**
00240                            expat end-node callback.
00241                         */
00242                         static void XMLCALL end_node( void *, const char * )
00243                         {
00244                                 if( ! m_name.empty() ) // property node
00245                                 {
00246                                         EXPATDEBUG << "Closing property node["<<m_name<<"="<<m_cbuf<<"]\n";
00247                                         m_builder.add_property( m_name, m_cbuf );
00248                                 }
00249                                 else
00250                                 { // object_node
00251                                         EXPATDEBUG << "Closing object node.\n";
00252                                         m_builder.close_node();
00253                                 }
00254                                 m_name = "";
00255                                 m_cbuf = "";
00256                         }
00257 
00258                         /**
00259                            expat char-data callback.
00260                         */
00261                         static void XMLCALL char_handler( void *, const char * txt, int len )
00262                         {
00263                                 if( m_name.empty() ) return; // we're not in a property.
00264                                 const char *c;
00265                                 for( int i = 0; i < len; i++ )
00266                                 {
00267                                         c = txt++;
00268                                         m_cbuf += *c;
00269                                 }
00270                                 EXPATDEBUG << "char_handler(...,"<<len<<"): m_cbuf=[" << m_cbuf << "]\n";
00271                         }
00272 
00273                         /**
00274                            Uses expat to try to parse a tree of nodes
00275                            from the given stream.
00276                         */
00277                        node_type * expat_parse_stream( std::istream & is )
00278                         {
00279                                 XML_Parser p = XML_ParserCreate(NULL);
00280                                 if (! p)
00281                                 {
00282                                         EXPATDEBUG <<  "Couldn't allocate memory for parser\n";
00283                                         return 0;
00284                                 }
00285                                 
00286                                 XML_SetElementHandler(p, start_node, end_node);
00287                                 XML_SetCharacterDataHandler( p, char_handler );
00288                                 m_builder.reset();
00289 
00290                                 m_name = "";
00291                                 m_cbuf = "";
00292 
00293                                 bool done = false;
00294                                 size_t len = 0;
00295                                 std::string buff;
00296                                 try
00297                                 {
00298                                         while( true )
00299                                         {
00300                                                 if( std::getline( is, buff ).eof() ) done = true;
00301                                                 len = buff.size();
00302                                                 if( 0 < len ) if (XML_Parse(p, buff.c_str(), len, done) == XML_STATUS_ERROR)
00303                                                 {
00304                                                         std::ostringstream err;
00305                                                         err << "Parse error at line "
00306                                                              << XML_GetCurrentLineNumber(p)
00307                                                              << ": "
00308                                                             << XML_ErrorString(XML_GetErrorCode(p))
00309                                                             << ": buffer=["<<buff<<"]";
00310 //                                                         EXPATDEBUG << err.str() << "\n";
00311                                                         throw s11n::io_exception( "%s:%d: Parse error at line %d: %s: buffer=[%s]",
00312                                           __FILE__,
00313                                           __LINE__<
00314                                           XML_GetCurrentLineNumber(p),
00315                                           XML_ErrorString(XML_GetErrorCode(p)),
00316                                           buff.c_str() );
00317                                                 }
00318                                                 if( done ) break;
00319                                         }
00320                                 }
00321                                 catch( std::runtime_error & ex )
00322                                 {
00323                                         CERR << "EXCEPTION while XML_Parsing input: " << ex.what() << "!\n";
00324                                         m_builder.auto_delete( true );
00325                                         m_builder.reset();
00326                                 }
00327                                 XML_ParserFree( p );
00328                                 m_builder.auto_delete( false );
00329                                 return m_builder.root_node();
00330                         }
00331 
00332 
00333                 private:
00334                         size_t m_depth;
00335                         static data_node_tree_builder<node_type> m_builder;
00336                         static std::string m_name; // current node's name
00337                         static std::string m_cbuf; // cdata buffer
00338                 };
00339                 template <typename NodeT> data_node_tree_builder<NodeT> expat_serializer<NodeT>::m_builder;
00340                 template <typename NodeT> std::string expat_serializer<NodeT>::m_name;
00341                 template <typename NodeT> std::string expat_serializer<NodeT>::m_cbuf;
00342 
00343         } // namespace io
00344 } // namespace s11n
00345 
00346 #undef EXPATDEBUG
00347 #undef EXPAT_CLASS_ATTRIBUTE
00348 #endif // s11n_EXPAT_SERIALIZER_HPP_INCLUDED

Generated on Wed Jun 4 21:44:19 2008 for libs11n by  doxygen 1.5.3