00001 #ifndef s11n_EXPAT_SERIALIZER_HPP_INCLUDED
00002 #define s11n_EXPAT_SERIALIZER_HPP_INCLUDED 1
00003
00004 #include <s11n.net/s11n/io/strtool.hpp>
00005 #include <s11n.net/s11n/s11n_debuggering_macros.hpp>
00006
00007 #include <s11n.net/s11n/io/data_node_format.hpp>
00008
00009 #include <s11n.net/s11n/traits.hpp>
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
00029
00030 struct expat_sharing_context {};
00031
00032 }
00033
00034 typedef std::map<std::string,std::string> entity_translation_map;
00035
00036
00037
00038
00039
00040 entity_translation_map & expat_serializer_translations();
00041
00042
00043
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
00053
00054
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;
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
00080
00081 virtual node_type * deserialize( std::istream & src )
00082 {
00083 return this->expat_parse_stream( src );
00084 }
00085
00086
00087
00088
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;
00102 }
00103
00104 private:
00105
00106
00107
00108
00109 bool serialize_impl( const node_type & src, std::ostream & dest )
00110 {
00111 typedef ::s11n::node_traits<node_type> NT;
00112
00113
00114 #define INDENT(LEVEL,ECHO) indent = ""; for( size_t i = 0; i < depth + LEVEL; i++ ) { indent += '\t'; if(ECHO) dest << '\t'; }
00115
00116
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 {
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 {
00180 if( !closed )
00181 {
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
00199 {
00200 dest << "/>";
00201 }
00202
00203 dest << '\n';
00204 if( 0 == depth )
00205 {
00206 dest.flush();
00207
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 {
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
00233 {
00234 m_name = name;
00235 EXPATDEBUG << "Opening property node["<<m_name<<"]\n";
00236 }
00237 }
00238
00239
00240
00241
00242 static void XMLCALL end_node( void *, const char * )
00243 {
00244 if( ! m_name.empty() )
00245 {
00246 EXPATDEBUG << "Closing property node["<<m_name<<"="<<m_cbuf<<"]\n";
00247 m_builder.add_property( m_name, m_cbuf );
00248 }
00249 else
00250 {
00251 EXPATDEBUG << "Closing object node.\n";
00252 m_builder.close_node();
00253 }
00254 m_name = "";
00255 m_cbuf = "";
00256 }
00257
00258
00259
00260
00261 static void XMLCALL char_handler( void *, const char * txt, int len )
00262 {
00263 if( m_name.empty() ) return;
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
00275
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
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;
00337 static std::string m_cbuf;
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 }
00344 }
00345
00346 #undef EXPATDEBUG
00347 #undef EXPAT_CLASS_ATTRIBUTE
00348 #endif // s11n_EXPAT_SERIALIZER_HPP_INCLUDED