From: stephan b. <sg...@us...> - 2004-12-26 04:04:42
|
Update of /cvsroot/pclasses/pclasses2/src/s11n/io In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv30469/src/s11n/io Added Files: data_node_format.h data_node_io.cpp data_node_io.h Makefile.toc reg_serializer.h serializers.h Log Message: egg: initial port of s11n::io layer, the parts which don't need FlexLexer and friends. It compiles and links, but is untested after factory-related changes. --- NEW FILE: data_node_io.cpp --- #include <memory> // auto_ptr #include <iostream> #include <fstream> #include <pclasses/s11n/io/data_node_io.h> namespace P { namespace s11n { namespace io { std::string get_magic_cookie( const std::string & src, bool AsFile ) { if( src.empty() ) return src; std::string ret; typedef std::auto_ptr<std::istream> AP; AP is; if( AsFile ) { is = AP( new std::ifstream( src.c_str() ) ); } else { is = AP( new std::istringstream(src) ); } // AP is = AP( zfstream::get_istream( src, AsFile ) ); // ^^^^ @fixme: removed because P doesn't have this support (yet) if( ! is.get() ) return ret; return ( std::getline( *is, ret ).eof() ? "" : ret ); } std::string get_magic_cookie( std::istream & is ) { std::string ret; if( std::getline( is, ret ).eof() ) return ""; return ret; } } // namespace io }} // namespace P::s11n --- NEW FILE: Makefile.toc --- #!/usr/bin/make -f include toc.make SUBDIRS = expat HEADERS = \ data_node_format.h \ data_node_io.h \ reg_serializer.h \ serializers.h DIST_FILES += $(HEADERS) INSTALL_PACKAGE_HEADERS += $(HEADERS) INSTALL_PACKAGE_HEADERS_DEST = $(INSTALL_PACKAGE_HEADERS_BASE)/s11n/io SYMLINK_HEADERS = $(INSTALL_PACKAGE_HEADERS) SYMLINK_HEADERS_DEST = $(top_srcdir)/include/pclasses/s11n/io include $(TOC_MAKESDIR)/SYMLINK_HEADERS.make SOURCES = data_node_io.cpp DIST_FILES += $(SOURCES) OBJECTS = data_node_io.o CLEAN_FILES += $(OBJECTS) all: SYMLINK_HEADERS subdirs $(OBJECTS) --- NEW FILE: reg_serializer.h --- //////////////////////////////////////////////////////////////////////// // "supermacro" code for doing some registration stuff for Serializers. // // See s11n::io::register_serializer<>() for a function which does the // same thing as this supermacro (it's easier to use and doesn't // add as much back-end overhead). // // License: Public Domain // Author: st...@s1... //////////////////////////////////////////////////////////////////////// // NOTE: this file does not use a conventional BLAH_H_INCLUDED guard. // Yes, that's on purpose. //////////////////////////////////////////////////////////////////////// // The s11n header files are expected to have been included by the // time this file is ever included. // // usage: // define: // SERIALIZER_TYPE serializer_class // SERIALIZER_BASE base_of_serializer (or SERIALIZER_TYPE) // SERIALIZER_MAGIC_COOKIE optional "#!/magic/cookie/string" // SERIALIZER_ALIAS optional "alias_string" for classloading // // #include <this_file> // // After each include all of these macros are unset so that they may // be immediately re-used for another registration. // // The cookie is required if s11n is to be able to load // your files transparently. An alias is convenient for s11nconvert // and such, but not required. //////////////////////////////////////////////////////////////////////// #include <stdlib.h> // abort() #define DEBUG_REG 0 #if DEBUG_REG # include <pclasses/s11n/s11n_debuggering_macros.h> #endif #ifndef SERIALIZER_TYPE # error "SERIALIZER_TYPE is not set. Set it to the type you want to proxy before including this file!" #endif #ifndef SERIALIZER_NAME # error "SERIALIZER_NAME must be set to the string form of SERIALIZER_TYPE" #endif #ifndef SERIALIZER_BASE # error "SERIALIZER_BASE must be the base-most type of SERIALIZER_TYPE (may be the same)." #endif #include <pclasses/s11n/classload.h> namespace { // anonymous namespace is important for complex linking reasons. //////////////////////////////////////////////////////////////////////// // A utility to keep us from having to hard-code or macro-ize the class // name (macroing won't work with commas in the class names, anyway). template <> struct class_name< SERIALIZER_TYPE > { static const char * name() { return SERIALIZER_NAME; } }; #ifndef p_SERIALIZER_REG_CONTEXT_DEFINED #define p_SERIALIZER_REG_CONTEXT_DEFINED 1 /////////////////////////////////////////////////////////////// // we must not include this more than once per compilation unit /////////////////////////////////////////////////////////////// // A unique (per SERIALIZER_BASE/per compilation unit) space to assign // a bogus value for classloader registration purposes (see // the classloader docs for a full description of how this // works). template <typename Context> struct serializer_reg_context { typedef Context context; static bool placeholder; static void reg() { CERR << "ACHTUNG: << " << ::classname< serializer_reg_context<context> >() << " is not specialized, which means that registration hasn't been done.\n" << "For instructions see: " << __FILE__ << "\n"; abort(); } }; template <typename Context> bool serializer_reg_context<Context>::placeholder = false; #endif // !s11n_SERIALIZER_REG_CONTEXT_DEFINED //////////////////////////////////////////////////////////////////////////////// template <> struct serializer_reg_context< SERIALIZER_TYPE > { typedef SERIALIZER_TYPE context; static bool placeholder; static void reg() { std::string cname = ::classname< context >(); #if DEBUG_REG CERR << "\nRegistering Serializer: " << SERIALIZER_NAME << "\n" << "classname() says: " << cname << "\n" # ifdef SERIALIZER_MAGIC_COOKIE << "cookie="<< SERIALIZER_MAGIC_COOKIE << "\n" # endif # ifdef SERIALIZER_ALIAS << "alias="<< SERIALIZER_ALIAS << "\n" # endif ; // CERR #endif // DEBUG_REG #ifdef SERIALIZER_ABSTRACT ::P::s11n::cl::classloader_register_abstract< SERIALIZER_BASE >( cname ); # undef SERIALIZER_ABSTRACT #else ::P::s11n::cl::classloader_register< SERIALIZER_BASE, SERIALIZER_TYPE >( cname ); ::P::s11n::cl::classloader_register< SERIALIZER_TYPE, SERIALIZER_TYPE >( cname ); #endif #ifdef SERIALIZER_MAGIC_COOKIE // @fixme: P factory can't alias yet: // cllite::alias< SERIALIZER_BASE >( SERIALIZER_MAGIC_COOKIE, cname ); // cllite::alias< SERIALIZER_TYPE >( SERIALIZER_MAGIC_COOKIE, cname ); # undef SERIALIZER_MAGIC_COOKIE #endif #ifdef SERIALIZER_ALIAS // @fixme: P factory can't alias yet: // cllite::alias< SERIALIZER_BASE >( SERIALIZER_ALIAS, cname ); // cllite::alias< SERIALIZER_TYPE >( SERIALIZER_ALIAS, cname ); # undef SERIALIZER_ALIAS #endif } }; bool serializer_reg_context< SERIALIZER_TYPE >::placeholder = ( serializer_reg_context< SERIALIZER_TYPE >::reg() , true ); } // anon namespace //////////////////////////////////////////////////////////////////////////////// // end proxy code for [SERIALIZER] //////////////////////////////////////////////////////////////////////////////// #undef SERIALIZER_TYPE #undef SERIALIZER_NAME #undef DEBUG_REG --- NEW FILE: data_node_format.h --- #ifndef p_DATA_NODE_FORMAT_H_INCLUDED #define p_DATA_NODE_FORMAT_H_INCLUDED //////////////////////////////////////////////////////////////////////////////// // data_node_format.h // Contains some helpers related to parsing/formating data_node-style objects. // // License: Public Domain // Author: st...@s1... //////////////////////////////////////////////////////////////////////////////// #include <string> #include <list> #include <map> #include <stdexcept> // #include <typeinfo> // #include <s11n.net/zfstream/zfstream.h> // get_i/ostream() // #include <s11n.net/tostring/to_string.h> // to/from_string() // #include <s11n.net/stringutil/string_util.h> // translate_entities() #include <pclasses/s11n/s11n_debuggering_macros.h> // COUT/CERR #include <pclasses/s11n/classload.h> // classload() #include <pclasses/s11n/data_node_functor.h> // some utility functors #include <pclasses/s11n/data_node_serialize.h> // data_node_serializer<> and friends #include <pclasses/s11n/traits.h> #include <pclasses/s11n/io/data_node_io.h> // default serializer interfaces //////////////////////////////////////////////////////////////////////////////// // NO DEPS ON data_node.h ALLOWED! //////////////////////////////////////////////////////////////////////////////// namespace P { namespace s11n { namespace io { /** A typedef representing a map of tokens used for "entity translations" by s11n parsers/serializers. */ typedef std::map<std::string,std::string> entity_translation_map; /** tree_builder exists mainly so some lex-based code can get access to a non-templated type (so we don't have to hard-code the parsers to a node_type). It provides only the interface needed by the current lex-based parsers, not some ultimately reusable interface. It is not functionally useful by itself - it must be subclassed and all of it's virtual methods must be implemented. */ class tree_builder { public: tree_builder() : m_autodel(true) {} virtual ~tree_builder() {} /** Starts a new node with the the given class name and node name. Return value indicates success or failure. */ virtual bool open_node( const std::string & classname, const std::string & nodename ) = 0; /** Closes the current node. Return value indicates success or failure. */ virtual bool close_node() = 0; /** Sets property key to val for the current node. Return value indicates success or failure. */ virtual bool add_property( const std::string & key, const std::string & val ) = 0; /** Returns the depth level of the parser, where the root node is 1. */ virtual size_t node_depth() const = 0; /** Changes the implementation class name of the current node. */ virtual bool change_node_class( const std::string & newclassname ) = 0; /** If auto_delete() is on (the default) then this object should delete it's children when it is destroyed, otherwise it will not. It is up to subclasses to honor this, as this base type does no handling of children. */ void auto_delete( bool b ) { this->m_autodel = b; } /** This is the getter for auto_delete( bool ). */ bool auto_delete() const { return this->m_autodel; } private: bool m_autodel; }; /** data_node_tree_builder is a helper class for building trees from deserialized data, designed particularly for use with lex/callback-based tree builders. It owns all objects which build up it's tree. If you want them you must manually remove them from the container. You normally do not want them, however - they're mostly throwaway nodes on their way to becoming fully deserialized objects. This class only provides methods for building a tree, not for traversing it. Once you have built a tree, traverse it starting at the root_node(). Based on usage conventions this type supports only a single root node. */ template <typename NodeType> class data_node_tree_builder : public tree_builder { public: typedef NodeType node_type; typedef std::list< node_type * > child_list_type; /** Creates a default builder. */ data_node_tree_builder() : m_node_count(0), m_node(0),m_root(0) { } /** Deletes this object's children if auto_delete() returns true. */ virtual ~data_node_tree_builder() { if( this->auto_delete() && this->m_root ) { //CERR << "data_node_tree_builder<> cleaning up root node.\n"; delete( this->m_root ); } else { //CERR << "data_node_tree_builder<> was relieved of child duty (or had no child).\n"; } } /** Opens a new node, making that the current node. classname will be used for the node's impl_class() (see docs for node_type::impl_class()). name will be the object's name, which is important for de/serializing the node (see node_type::name()). It returns false on error, else true. The default implementation has no error conditions, and therefor always returns true. Node that classnames and node names need not be unique (nor make up unique combinations). Any number of nodes may have the same name or classname. */ bool open_node( const std::string & classname, const std::string & nodename ) { ++m_node_count; this->m_node = ( this->m_nodestack.empty() ? 0 : this->m_nodestack.back() ); typedef ::P::s11n::node_traits<node_type> NTR; node_type * newnode = new node_type(); if ( m_node ) { // if we're in a node, add new node as a child to that one: NTR::children( *m_node ).push_back( newnode ); } this->m_node = newnode; NTR::name( *m_node, nodename ); NTR::class_name( *m_node, classname ); this->m_nodestack.push_back( m_node ); bool ret = true; if ( 1 == this->m_nodestack.size() ) { if( m_root ) { CERR << "open_node("<<classname<<","<<nodename<<") WARNING: deleting extra root node!\n"; delete( m_node ); ret = false; } else { m_root = m_node; } } return ret; } /** Closes the most-recently-opened node, effectively popping the previous node off of the node stack (it is not destroyed). It is an error to call this more often than calling open_node(). It returns false on error (e.g., called with no opened node). */ virtual bool close_node() { if ( !m_node || m_nodestack.empty() ) { CERR << "close_node() error: called with an empty node stack!" << std::endl; return false; } m_nodestack.pop_back(); if ( m_nodestack.empty() ) { m_node = NULL; } else { m_node = m_nodestack.back(); } return true; } /** Adds the given key/value pair to the current node and returns true. If no node is currently opened it returns false. */ virtual bool add_property( const std::string & key, const std::string & val ) { if( ! this->m_node ) return false; typedef ::P::s11n::node_traits<node_type> NTR; NTR::set( *m_node, key, val ); return true; } /** Returns the total number of nodes opened via open_node(). */ size_t node_count() const { return m_node_count; } /** Returns the current depth of opened nodes. A return value of 1 means the current node is the root node, for example, and 0 means that no node has yet been opened. */ size_t node_depth() const { return m_nodestack.size(); } /** Returns the most recent root node parsed out of the input object. Use auto_delete() to determine ownership of the returned pointer. */ node_type * root_node() const { return m_root; } /** Returns the current node. Use auto_delete() to determine ownership of the returned pointer. */ node_type * current_node() const { return m_node; } /** Changes class name of current node, if one is set. Returns false only if no node is currently opened, else it returns true. */ virtual bool change_node_class( const std::string & newclassname ) { if( ! this->m_node ) return false; typedef ::P::s11n::node_traits<node_type> NTR; NTR::class_name( *(this->m_node), newclassname ); return true; } /** Deletes this object's root_node() if auto_delete() is true. */ void reset() { if( this->auto_delete() && this->m_root ) delete this->m_root; this->m_root = 0; } private: size_t m_node_count; node_type * m_node; node_type * m_root; typedef std::deque < node_type * > node_stack; node_stack m_nodestack; }; /** A helper functor to loop over serializable children of a node from within a Serializer implementation. Designed for use with std::for_each(). SerializerT must be compatible with <code>data_node_serializer<></code>. */ template <typename SerializerT> struct node_child_simple_formatter { typedef SerializerT serializer_type; // typedef typename SerializerT::node_type node_type; /** Preconditions: - Ser must be valid references. - Both ser and os must outlive this object. More correctly, this object's operator() must not be called after either ser or os are destroyed. */ node_child_simple_formatter( serializer_type & ser, std::ostream & os, const std::string & prefix = "", const std::string & suffix = "\n" ) : m_ser(ser), m_os(&os), m_pre(prefix), m_suf(suffix) { } /** Serializes src into this object's target container, using this object's serializer. */ template <typename NodeType> bool operator()( const NodeType * src ) const { if( ! src ) return false; if( ! this->m_pre.empty() ) *(this->m_os) << this->m_pre; bool b = this->m_ser.serialize( *src, *(this->m_os) ); if( ! this->m_suf.empty() ) *(this->m_os) << this->m_suf; return b; } private: serializer_type & m_ser; std::ostream * m_os; std::string m_pre; std::string m_suf; }; } // namespace io }} // namespace P::s11n #endif // p_DATA_NODE_FORMAT_H_INCLUDED --- NEW FILE: serializers.h --- #ifndef p_SERIALIZERS_HPP_INCLUDED #define p_SERIALIZERS_HPP_INCLUDED 1 //////////////////////////////////////////////////////////////////////////////// // serializers.h: Some utility code for working with // s11n::io Serializer types. // // License: Public Domain // Author: st...@s1... //////////////////////////////////////////////////////////////////////////////// #include <pclasses/Factory.h> #include <pclasses/Plugin/Plugin.h> #include <pclasses/Phoenix.h> #include <pclasses/s11n/io/data_node_io.h> #include <pclasses/s11n/s11n_debuggering_macros.h> namespace P { namespace s11n { namespace io { template <typename NodeType> class serializer_plugin_manager : public ::P::Plugin::PluginManager< data_node_serializer< NodeType > > { public: serializer_plugin_manager(){} virtual ~serializer_plugin_manager(){} }; } // P::s11n::io } // P::s11n namespace Hook { /** This hook will cause calls to Factory::instance() to return a serializer_plugin_manager instance in disguise. */ template < typename NodeT > struct FactoryInstanceHook< ::P::Factory< ::P::s11n::io::data_node_serializer<NodeT>, ::P::Sharing::FactoryContext, std::string > > { typedef ::P::Factory< ::P::s11n::io::data_node_serializer<NodeT>, ::P::Sharing::FactoryContext, std::string > FactoryType; typedef ::P::s11n::io::serializer_plugin_manager< NodeT > RealFactoryType; typedef FactoryInstanceHook<FactoryType> ThisType; /** Initializes the passed-in factory. i.e., it does nothing. It's here for demonstration purposes. */ void operator()( FactoryType & ) throw() { CERR << "Initializing a PluginManager instance() we hacked in via FactoryInstanceHook!\n"; } /** The default implementation returns a shared Factory object. THIS type's operator() will be called on the factory immediately after creating the factory. */ static FactoryType & instance() { typedef ::P::Phoenix<RealFactoryType, ::P::Sharing::FactoryContext, ThisType > PHX; return PHX::instance(); } }; } // P::Hook namespace s11n { namespace io { /** Populates target list with the names of registered Serializers. ListT must support push_back( std::string ). If onlyShortNames is true (the default) then only "simple" names are put into target, not the "full" names of the classes. This is to make the data more useful in the context of client applications as, e.g., a list of choices for users. Note that only serializers extending from s11n::io::data_node_serializer<NodeT> are returned. If onlyShortNames is true then only names which contain only alphanumeric or underscore characters are returned. This is used for filtering out all names except Serializer aliases, excluding the full class names and any magic cookie aliases. */ template <typename NodeT, typename ListT> void serializer_list( ListT & target, bool onlyShortNames = true ) { CERR << "FIXME: serializer_list() not yet fixed to work with P::Factory.\n"; return; // typedef ::P::s11n::io::data_node_serializer<NodeT> BaseSerT; // typedef ::cl::class_loader< BaseSerT > SerCL; // typedef typename SerCL::alias_map AMap; // #define SERCL ::P::s11n::cl::classloader< BaseSerT >() // typename AMap::const_iterator cit = SERCL.aliases().begin(), // cet = SERCL.aliases().end(); // #undef SERCL // std::string alias; // static const std::string nonspecial = // "_0123456789abcdefghijklmnopqrstuvwqxyzABCDEFGHIJKLMNOPQRSTUVWQXYZ"; // for( ; cet != cit; ++cit ) // { // alias = (*cit).first; // if( onlyShortNames ) // { // filter out all but "simple" names: // if( std::string::npos != // alias.find_first_not_of( nonspecial ) // ) // { // continue; // } // } // target.push_back( alias ); // } } /** Registers a Serializer type with the s11n::io layer. It must: - be templatized on a DataNodeType - subclass s11n::io::data_node_serializer<DataNodeType> - provide a node_type typedef which is the same as DataNodeType i.e., the conventions of all of the Serializers included with libs11n. Registering makes the type available to the data_node_serializer classloader. Arguments: - classname = SerializerT's stringified class name. - alias = a "friendly" name for the SerializerT. SerializeT's magic_cookie() function is called to alias the cookie as an alias for classname. Thus, a SerializerT object is (briefly) instantiated. Node that this functions essentially performs the same operations as the reg_serializer.h supermacro, and the two should be equivalent (though this seems easier to use). */ template <typename SerializerT> void register_serializer( const std::string & classname, const std::string & alias ) { typedef SerializerT ST; typedef typename ST::node_type NT; typedef data_node_serializer<NT> BaseST; // typedef data_node_serializer<NodeT> * BaseSerializerType; // CERR << "FIXME: register_serializer() not yet fixed to work with P::Factory.\n"; // return; #define PM pluginManager< BaseST >() #define FACCREATE ::P::Hook::FactoryCreateHook<BaseST,ST>::create using namespace ::P::Plugin; static bool inited = false; if( ! inited && (inited=true) ) { PM.searchPath().addExtension( "_serializer.so" ); PM.searchPath().addExtension( "_serializer.dll" ); // cllite::class_path().add_extension( "_serializer.so" ); // cllite::class_path().add_extension( "_serializer.dll" ); } CERR << "register_serializer(" << classname << ","<<alias<<")\n"; PM.registerFactory( classname, FACCREATE ); PM.registerFactory( alias, FACCREATE ); PM.registerFactory( ST().magic_cookie(), FACCREATE ); #undef PM #undef FACCREATE // ::P::s11n::cl::classloader_register< BaseST, ST >( classname ); // ::P::s11n::cl::classloader< BaseST >().alias( alias, classname ); // ::P::s11n::cl::classloader< BaseST >().alias( ST().magic_cookie(), classname ); } /** Returns a Serializer object, which must have been registered with s11n::cl::classloader< s11n::io::data_node_serializer<NodeT> >(). The caller owns the returned pointer, which may be 0. */ template <typename NodeT> s11n::io::data_node_serializer<NodeT> * create_serializer( const std::string & classname ) { // typedef serializer_plugin_manager<NodeT> SPM; try { CERR << "create_serializer("<<classname<<") trying plugin manager...\n"; // return SPM::instance().create( classname ); return ::P::s11n::cl::classloader< SPM >().create( classname ); } catch(const ::P::Exception & e) { CERR << "Exception while loading serializer '"<<classname<<"': " << e.what() << "\n"; } return 0; // typedef s11n::io::data_node_serializer<NodeT> BaseSerT; // BaseSerT * s = 0; // if( ( s = s11n::cl::classload< BaseSerT >( classname ) ) ) // { // return s; // } // static const char * addon = "_serializer"; // if( (std::string::npos == classname.find(addon)) ) // try harder! // { // std::string harder = classname + addon; // //CERR << "Trying harder for " << classname << " --> " << harder << "!\n"; // s = create_serializer<NodeT>( harder ); // } // //CERR << "Harder try= " << std::hex << s << "\n"; // return s; } }} // namespace P::s11n::io } // namespace P::s11n #endif // p_SERIALIZERS_HPP_INCLUDED --- NEW FILE: data_node_io.h --- #ifndef p_DATA_NODE_IO_H_INCLUDED #define p_DATA_NODE_IO_H_INCLUDED //////////////////////////////////////////////////////////////////////// // data_node_io.h // some i/o interfaces & helpers for s11n // License: Public Domain // Author: st...@s1... //////////////////////////////////////////////////////////////////////// #include <string> #include <sstream> #include <list> #include <map> #include <deque> #include <iostream> #include <fstream> #include <memory>// auto_ptr #include <cassert> #include <typeinfo> #include <pclasses/Phoenix.h> // phoenix class #include <pclasses/Plugin/Plugin.h> #include <pclasses/System/SharedLib.h> // openSharedLib() #include <pclasses/s11n/s11n_debuggering_macros.h> // COUT/CERR #include <pclasses/s11n/classload.h> // classloader() #include <pclasses/s11n/data_node_serialize.h> // unfortunately dep #include <pclasses/s11n/traits.h> // s11n_traits & node_traits //////////////////////////////////////////////////////////////////////////////// // NO DEPS ON data_node.h ALLOWED! //////////////////////////////////////////////////////////////////////////////// namespace P { namespace s11n { namespace io { /** Convenience function for grabbing the first line of a file. If AsFile == true then returns the first line of the file, else returns up to the first newline of src. */ std::string get_magic_cookie( const std::string & src, bool AsFile = true ); /** Convenience function for grabbing the first line of a stream. Returns the first line of the given stream, or an empty string on error. */ std::string get_magic_cookie( std::istream & is ); /** data_node_serializer provides an interface for saving/loading a given abstract data node type to/from streams. It is designed for containers which comply with s11n's Data Node interface and conventions. Conventions: Must provide: typedef NodeT node_type Two de/serialize functions, following the stream-based interface shown here (filename-based variants are optional, but convenient for clients). */ template <typename NodeT> class data_node_serializer { public: /** The underlying data type used to store serialized data. */ typedef NodeT node_type; data_node_serializer() { this->magic_cookie( "WARNING: magic_cookie() not set!" ); // ^^^ subclasses must do this. this->metadata().name( "serializer_metadata" ); }; virtual ~data_node_serializer(){}; /** A convenience typedef, mainly for subclasses. */ typedef std::map<std::string,std::string> translation_map; /** Returns a map intended for use with stringutil::translate_entities(). The default implementation returns an empty map. Subclasses should override this to return a translation map, if they need one. The default map is empty. Be aware that this may very well be called post-main(), so subclasses should take that into account and provide post-main()-safe maps! (Tip: see phoenix::phoenix.) */ virtual const translation_map & entity_translations() const { typedef ::P::Phoenix<translation_map,data_node_serializer<node_type> > TMap; return TMap::instance(); } /** Must be implemented to format node_type to the given ostream. It should return true on success, false on error. The default implementation always returns false. Note that this function does not use s11n::serialize() in any way, and is only coincidentally related to it. */ virtual bool serialize( const node_type & /*src*/, std::ostream & /*dest*/ ) { return false; } /** Overloaded to save dest to the given filename. The default implementation treats destfile as a file name and passes the call on to serialize(node_type,ostream). Returns true on success, false on error. This function is virtual so that Serializers which do not deal with i/ostreams (e.g., those which use a database connection) can override it to interpret destfile as, e.g., a database-related string (e.g., connection, db object name, or whatever). */ virtual bool serialize( const node_type & src, const std::string & destfile ) { std::ofstream os( destfile.c_str() ); // @fixme: zfstream support was removed for P! if( os.good() ) return false; bool b = this->serialize( src, os ); return b; } /** Must be implemented to parse a node_type from the given istream. It should return true on success, false on error. The default implementation always returns 0 and does nothing. Note that this function does not use s11n::deserialize() in any way, and is only coincidentally related to it. */ virtual node_type * deserialize( std::istream & ) { return 0; } /** Overloaded to load dest from the given filename. It supports zlib/bz2lib decompression for files if your s11n lib supports them. This is virtual for the same reason as serialize(string). */ virtual node_type * deserialize( const std::string & src ) { // typedef std::auto_ptr<std::istream> AP; // AP is = AP( zfstream::get_istream( src ) ); // if( ! is.get() ) return 0; // @fixme: ^^^ zfstream removed for P std::ifstream is(src.c_str()); if( ! is.good() ) return 0; return this->deserialize( is ); } /** Gets this object's magic cookie. Cookies are registered with <code>class_loader< data_node_serializer<NodeType> ></code> types to map files to file input parsers. */ std::string magic_cookie() const { return this->m_cookie; } protected: /** Sets the magic cookie for this type. */ void magic_cookie( const std::string & c ) { this->m_cookie = c; } /** metadata is an experimental feature allowing serializers to store arbitrary serializer-specific information in their data steams. */ node_type & metadata() { return this->m_meta; } /** A const overload of metadata(). */ const node_type & metadata() const { return this->m_meta;} private: std::string m_cookie; node_type m_meta; }; // data_node_serializer<> /** Tries to load a NodeType object from the given node. It uses the cookie from the input stream and classload<SerializerBaseType>() to find a matching Serializer. 0 is returned on error, else a new pointer, which the caller owns. Achtung: the first line of input from the input stream is consumed by this function (to find the cookie), and the cookie is not passed on to the handler! The only reliable way around this [that i know of] is to buffer the whole input as a string, and i don't wanna do that (it's bad for massive data files). ACHTUNG: Only usable for loading ROOT nodes. Special feature: If the first line of the stream is "#s11n::io::load_serializer" then the next token on that line is expected to be a Serializer class name. This function will try to classload that object. If successful it will use that type to deserialize the input string. If unsuccessful it will read the next line for a cookie and try to use that Serializer. If that fails, it will return 0. */ template <typename NodeType> NodeType * load_node_classload_serializer( std::istream & is ) { typedef data_node_serializer<NodeType> ST; std::string cookie; // = get_magic_cookie( is ); // CERR << "cookie="<<cookie<<std::endl; if( ! std::getline( is, cookie ) ) { CERR << "Odd: got a null cookie from the istream.\n"; return 0; // happens post-main() on valid streams sometimes!?!?! } typedef std::auto_ptr<ST> AP; AP ser; static const std::string opencmd = "#s11n::io::serializer"; std::string::size_type at = cookie.find( opencmd ); while( 0 == at ) { std::string dll = cookie.substr( opencmd.size()+1 ); //CERR << "Trying to load Serializer from cookie: " << dll << "\n"; ser = AP( ::P::s11n::cl::classload<ST>( dll ) ); if( ser.get() ) { // if we load that Serializer, use it on the rest of the data... break; } std::string where; try { where = ::P::Plugin::findPlugin<ST>( dll ); if( ! where.empty() ) { ::P::System::openSharedLib( where ); } } catch( const ::P::Exception & ex ) { CERR << "Error opening DLL: " << dll << ": " << ex.what()<<"\n"; } if( where.empty() ) { CERR <<opencmd << " Serializer class ["<<dll<<"] could not be found. Continuing and hoping for the best...\n"; } return load_node_classload_serializer<NodeType>( is ); } if( ! ser.get() ) { ser = AP( ::P::s11n::cl::classload<ST>( cookie ) ); } if( ! (ser.get()) ) { CERR << "Did not find serializer for cookie ["<<cookie<<"]."<<std::endl; return NULL; } // CERR << "Dispatching to node loader for cookie ["<<cookie<<"]\n"; return ser->deserialize( is ); } /** Returns a node pointer, parsed from the given stream, using <code>s11n::io::data_node_serializer<NodeType></code> as the base type for looking up a stream handler. ACHTUNG: Only usable for loading ROOT nodes. */ template <typename NodeType> NodeType * load_node( std::istream & is ) { return load_node_classload_serializer< NodeType >( is ); } /** Overloaded form of load_node( istream ), provided for convenience. If AsFile is true, input is treated as a file, otherwise it is treated as a string containing input to parse. ACHTUNG: Only usable for loading ROOT nodes. Maintenance note: AsFile==false may be extremely inefficient, as src may get copied one additional time. */ template <typename NodeType> NodeType * load_node( const std::string & src, bool AsFile = true ) { typedef std::auto_ptr<std::istream> AP; AP is; if( AsFile ) { is = AP( new std::ifstream( src.c_str() ) ); } else { is = AP( new std::istringstream(src) ); } // AP is = AP( zfstream::get_istream( src, AsFile ) ); // ^^^^ @fixme: zfstream removed because P doesn't have this support (yet) if( ! is.get() ) return 0; return load_node<NodeType>( *is ); } /** Tries to load a SerializableT from the given stream. On success returns a new object, else 0. The caller owns the returned pointer. ACHTUNG: Only usable for loading ROOT nodes. */ template <typename NodeT,typename SerializableT> SerializableT * load_serializable( std::istream & src ) { typedef std::auto_ptr<NodeT> AP; AP node = AP( load_node<NodeT>( src ) ); if( ! node.get() ) { CERR << "load_serializable<>(istream) Could not load a root node from the input.\n"; return 0; } return s11n::deserialize<NodeT,SerializableT>( *node ); } /** An overloaded form which takes an input string. If AsFile is true the string is treated as a file name, otherwise it is processed as an input stream. ACHTUNG: Only usable for loading ROOT nodes. */ template <typename NodeT,typename SerializableT> SerializableT * load_serializable( const std::string & src, bool AsFile = true ) { typedef std::auto_ptr<std::istream> AP; if( AsFile ) { is = AP( new std::ifstream( src.c_str() ) ); } else { is = AP( new std::istringstream(src) ); } // AP is = AP( zfstream::get_istream( src, AsFile ) ); // @fixme: ^^^ zfstream removed for P. if( ! is.get() ) { CERR << "load_serializable<>(string) Could not open stream!.\n"; return 0; } return load_serializable<NodeT,SerializableT>( is ); } /** Saves src to the given ostream using the given Serializer type. ONLY use this for saving root nodes! */ template <typename SerializerT> bool save_node( const typename SerializerT::node_type & src, std::ostream & dest ) { return SerializerT().serialize( src, dest ); } /** Saves src, a Serializable type, to the given ostream using a SerializerT serializer. SerializerT must be compatible with s11n::io::data_node_serializer<> conventions and must provide a <code>typedef XXX node_type</code>, where XXX is a data type conforming to s11n::data_node conventions. Returns true on success, false on error. ONLY use this for saving root nodes! */ template <typename SerializerT,typename SerializableT> bool save_serializable( const SerializableT & src, std::ostream & dest ) { typedef typename SerializerT::node_type node_type; node_type node( "serializable" ); if( ! ::P::s11n::serialize<node_type,SerializableT>( node, src ) ) return false; return SerializerT().serialize( node, dest ); } /** An overloaded form which takes a filename. ONLY use this for saving root nodes! */ template <typename SerializerT,typename SerializableT> bool save_serializable( const SerializableT & src, const std::string & filename ) { // typedef std::auto_ptr<std::ostream> AP; // AP os = AP( zfstream::get_ostream( filename ) ); // if( ! os.get() ) return false; // @fixme: ^^^ zfstream removed for P std::ofstream os( filename.c_str() ); if( ! os.good() ) return false; return save_serializable<SerializerT,SerializableT>( src, os ); } } // namespace io }} // namespace P::s11n #endif // p_DATA_NODE_IO_H_INCLUDED |