Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

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                            Writes src out to dest.
00080                         */
00081                         virtual bool serialize( const node_type & src, std::ostream & dest )
00082                         {
00083                                 typedef ::s11n::node_traits<node_type> NT;
00084 
00085 // INDENT() is a helper macro for some serializers.
00086 #define INDENT(LEVEL,ECHO) indent = ""; for( size_t i = 0; i < depth + LEVEL; i++ ) { indent += '\t'; if(ECHO) buff << '\t'; }
00087 
00088                                 std::ostringstream buff; // we buffer so we can handle self-closing nodes
00089                                 size_t depth = this->m_depth++;
00090                                 if ( 0 == depth )
00091                                 {
00092                                         buff << this->magic_cookie() << '\n';
00093                                 }
00094 
00095 
00096                                 std::string nname = NT::name(src);
00097                 if( ! is_valid_xml_key( nname ) )
00098                 {
00099                     std::ostringstream err;
00100                     err << "expat_serializer::serialize(): node name '"
00101                         << nname << "' is not a valid XML tag name.";
00102                     throw s11n::io_exception( err.str(), __FILE__, __LINE__ );
00103                 }
00104 
00105                                 std::string impl = NT::class_name(src);
00106                                 ::s11n::io::strtool::translate_entities( impl, expat_serializer_translations(), false );
00107                                 std::string indent;
00108                                 buff << "<" << nname << " "<<EXPAT_CLASS_ATTRIBUTE<<"=\""<< impl <<"\"";
00109                                 bool closed = false;
00110                                 typedef typename NT::property_map_type PMT;
00111                 typedef typename PMT::const_iterator CHIT;
00112                 CHIT cit, cet;
00113                 cit = NT::properties(src).begin();
00114                 cet = NT::properties(src).end();
00115                                 std::string propval;
00116                                 std::string key;
00117 
00118                                 if( cet != cit )
00119                                 { // got properties?
00120                                         closed = true;
00121                                         buff << ">\n";
00122                                         INDENT(1,0);
00123                                         for ( ; cet != cit; ++cit )
00124                                         {
00125                                                 key = ( *cit ).first;
00126                         if( ! is_valid_xml_key( key ) )
00127                         {
00128                             std::ostringstream err;
00129                             err << "expat_serializer::serialize(): key '"
00130                                 << key << "' is not a valid XML tag name.";
00131                             throw s11n::io_exception( err.str(), __FILE__, __LINE__ );
00132                         }
00133                                                 propval = ( *cit ).second;
00134                                                 buff << indent << "<" << key;
00135                                                 if( propval.empty() )
00136                                                 {
00137                                                         buff << "/>";
00138                                                 }
00139                                                 else
00140                                                 {
00141                                                         buff << ">";
00142                                                         ::s11n::io::strtool::translate_entities( propval, expat_serializer_translations(), false );
00143                                                         buff << propval;
00144                                                         buff << "</" << key << ">";
00145                                                 }
00146                                                 buff << "\n";
00147                                         }
00148                                 }
00149 
00150                 
00151                 typedef typename NT::child_list_type CHLT;
00152                                 typename CHLT::const_iterator chit = NT::children(src).begin(),
00153                                         chet = NT::children(src).end();
00154                                 if( chet != chit )
00155                                 { // got kids?
00156                                         if( !closed )
00157                                         { // close node opener
00158                                                 buff << ">\n";
00159                                                 closed = true;
00160                                         }
00161                                         INDENT(1,0);
00162                                         std::for_each( chit, chet,
00163                                                        node_child_simple_formatter<this_type>( *this,
00164                                                                                                buff,
00165                                                                                                indent,
00166                                                                                                "" )
00167                                                        );
00168                                 }
00169 
00170                                 if( closed )
00171                                 {
00172                                         INDENT(0,1);
00173                                         buff << "</" << nname << ">";
00174                                 }
00175                                 else // self-close node
00176                                 {
00177                                         buff << "/>";
00178                                 }
00179 
00180                                 dest << buff.str() << "\n";
00181                                 if( 0 == depth )
00182                                 {
00183                                         dest.flush();
00184                                         // if we don't do this then the client is possibly forced to flush() the stream :/
00185                                 }
00186                                 --this->m_depth;
00187                                 return true;
00188 #undef INDENT
00189                         }
00190 
00191                         static void XMLCALL start_node( void *, const char * name, const char ** attr )
00192                         {
00193                                 m_cbuf = "";
00194                                 if( attr[0] )
00195                                 { // object node
00196                                         std::string clname = "WTF?";
00197                                         const std::string classnameattr = std::string(EXPAT_CLASS_ATTRIBUTE);
00198                                         for( int i = 0; attr[i]; i += 2 )
00199                                         {
00200                                                 if( attr[i] == classnameattr )
00201                                                 {
00202                                                         clname = attr[i+1];
00203                                                         break;
00204                                                 }
00205                                         }
00206                                         EXPATDEBUG << "Opening object node["<<clname<<","<<name<<"]\n";
00207                                         m_builder.open_node( clname, name );
00208                                 }
00209                                 else // property node
00210                                 {
00211                                         m_name = name;
00212                                         EXPATDEBUG << "Opening property node["<<m_name<<"]\n";
00213                                 }
00214                         }
00215 
00216                         /**
00217                            expat end-node callback.
00218                         */
00219                         static void XMLCALL end_node( void *, const char * )
00220                         {
00221                                 if( ! m_name.empty() ) // property node
00222                                 {
00223                                         EXPATDEBUG << "Closing property node["<<m_name<<"="<<m_cbuf<<"]\n";
00224                                         m_builder.add_property( m_name, m_cbuf );
00225                                 }
00226                                 else
00227                                 { // object_node
00228                                         EXPATDEBUG << "Closing object node.\n";
00229                                         m_builder.close_node();
00230                                 }
00231                                 m_name = "";
00232                                 m_cbuf = "";
00233                         }
00234 
00235                         /**
00236                            expat char-data callback.
00237                         */
00238                         static void XMLCALL char_handler( void *, const char * txt, int len )
00239                         {
00240                                 if( m_name.empty() ) return; // we're not in a property.
00241                                 const char *c;
00242                                 for( int i = 0; i < len; i++ )
00243                                 {
00244                                         c = txt++;
00245                                         m_cbuf += *c;
00246                                 }
00247                                 EXPATDEBUG << "char_handler(...,"<<len<<"): m_cbuf=[" << m_cbuf << "]\n";
00248                         }
00249 
00250                         /**
00251                            Uses expat to try to parse a tree of nodes
00252                            from the given stream.
00253                         */
00254                        node_type * expat_parse_stream( std::istream & is )
00255                         {
00256                                 XML_Parser p = XML_ParserCreate(NULL);
00257                                 if (! p)
00258                                 {
00259                                         EXPATDEBUG <<  "Couldn't allocate memory for parser\n";
00260                                         return 0;
00261                                 }
00262                                 
00263                                 XML_SetElementHandler(p, start_node, end_node);
00264                                 XML_SetCharacterDataHandler( p, char_handler );
00265                                 m_builder.reset();
00266 
00267                                 m_name = "";
00268                                 m_cbuf = "";
00269 
00270                                 bool done = false;
00271                                 size_t len = 0;
00272                                 std::string buff;
00273                                 try
00274                                 {
00275                                         while( true )
00276                                         {
00277                                                 if( std::getline( is, buff ).eof() ) done = true;
00278                                                 len = buff.size();
00279                                                 if( 0 < len ) if (XML_Parse(p, buff.c_str(), len, done) == XML_STATUS_ERROR)
00280                                                 {
00281                                                         std::ostringstream err;
00282                                                         err << "Parse error at line "
00283                                                              << XML_GetCurrentLineNumber(p)
00284                                                              << ": "
00285                                                             << XML_ErrorString(XML_GetErrorCode(p))
00286                                                             << ": buffer=["<<buff<<"]";
00287 //                                                         EXPATDEBUG << err.str() << "\n";
00288                                                         throw std::runtime_error( err.str() );
00289                                                 }
00290                                                 if( done ) break;
00291                                         }
00292                                 }
00293                                 catch( std::runtime_error & ex )
00294                                 {
00295                                         CERR << "EXCEPTION while XML_Parsing input: " << ex.what() << "!\n";
00296                                         m_builder.auto_delete( true );
00297                                         m_builder.reset();
00298                                 }
00299                                 XML_ParserFree( p );
00300                                 m_builder.auto_delete( false );
00301                                 return m_builder.root_node();
00302                         }
00303 
00304 
00305                         /**
00306                            Overridden to parse src using this object's grammar rules.
00307                         */
00308                         virtual node_type * deserialize( std::istream & src )
00309                         {
00310                                 return this->expat_parse_stream( src );
00311                         }
00312 
00313  
00314                 private:
00315                         size_t m_depth;
00316                         static data_node_tree_builder<node_type> m_builder;
00317                         static std::string m_name; // current node's name
00318                         static std::string m_cbuf; // cdata buffer
00319                 };
00320                 template <typename NodeT> data_node_tree_builder<NodeT> expat_serializer<NodeT>::m_builder;
00321                 template <typename NodeT> std::string expat_serializer<NodeT>::m_name;
00322                 template <typename NodeT> std::string expat_serializer<NodeT>::m_cbuf;
00323 
00324         } // namespace io
00325 } // namespace s11n
00326 
00327 #undef EXPATDEBUG
00328 #undef EXPAT_CLASS_ATTRIBUTE
00329 #endif // s11n_EXPAT_SERIALIZER_HPP_INCLUDED

Generated on Sat Dec 10 13:38:25 2005 for libs11n-1.2.1 by  doxygen 1.4.4