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 bool serialize( const node_type & src, std::ostream & dest )
00082 {
00083 typedef ::s11n::node_traits<node_type> NT;
00084
00085
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;
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 {
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 {
00156 if( !closed )
00157 {
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
00176 {
00177 buff << "/>";
00178 }
00179
00180 dest << buff.str() << "\n";
00181 if( 0 == depth )
00182 {
00183 dest.flush();
00184
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 {
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
00210 {
00211 m_name = name;
00212 EXPATDEBUG << "Opening property node["<<m_name<<"]\n";
00213 }
00214 }
00215
00216
00217
00218
00219 static void XMLCALL end_node( void *, const char * )
00220 {
00221 if( ! m_name.empty() )
00222 {
00223 EXPATDEBUG << "Closing property node["<<m_name<<"="<<m_cbuf<<"]\n";
00224 m_builder.add_property( m_name, m_cbuf );
00225 }
00226 else
00227 {
00228 EXPATDEBUG << "Closing object node.\n";
00229 m_builder.close_node();
00230 }
00231 m_name = "";
00232 m_cbuf = "";
00233 }
00234
00235
00236
00237
00238 static void XMLCALL char_handler( void *, const char * txt, int len )
00239 {
00240 if( m_name.empty() ) return;
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
00252
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
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
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;
00318 static std::string m_cbuf;
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 }
00325 }
00326
00327 #undef EXPATDEBUG
00328 #undef EXPAT_CLASS_ATTRIBUTE
00329 #endif // s11n_EXPAT_SERIALIZER_HPP_INCLUDED