#ifndef __loader_template_h__ #define __loader_template_h__ #include #include #include "handle.template.h" #include "logs.h" #include "refcounted.h" /*****************************************************************************/ /* Loader Class */ /* * A static template base class for managing loadable items. * Should be used with classes which derive from loadable_class. */ /*****************************************************************************/ template< class T > class loader_class { public: virtual ~loader_class( void ) {} static handle_class< T > get_handle_by_file_name( const std::string & ); static handle_class< T > get_unique_handle_by_file_name( const std::string & ); static handle_class< T > add_item( T * ); static void log_file_name_map( void ); protected: static std::map< std::string, handle_class< T > > file_name_to_handle_map; private: static handle_class< T > load_item( const std::string &, bool ); static void remove_item( const std::string ); }; /*****************************************************************************/ /* Implementation */ /*****************************************************************************/ /* * Return the handle at the location in file_name_to_handle_map specified by * the given file name. If no handle exists, load the corresponding item from * file, add it to the map, and then return a handle to it. */ template< class T > inline handle_class< T > loader_class< T >::get_handle_by_file_name( const std::string &file_name ) { if( file_name == "" ) { handle_class< T > invalid_handle; return invalid_handle; } std::map< std::string, handle_class< T > >::iterator itor = file_name_to_handle_map.find( file_name ); if( itor != file_name_to_handle_map.end() ) { handle_class< T > handle = itor->second; if( handle.valid() == true ) return handle; } return load_item( file_name, true ); } /* * Add a new item to the file_name_to_handle_map, then load it from the * original requested file name (not the possibly modified mapped name). * Then return a handle to the new item. */ template< class T > inline handle_class< T > loader_class< T >::get_unique_handle_by_file_name( const std::string &file_name ) { return load_item( file_name, false ); } /* * Given a pointer to an item, add a handle to the map at the item's file name. * Ensure that no two items share the same file name by appending [2], [3], etc * to the current file name where necessary. */ template< class T > inline handle_class< T > loader_class< T >::add_item( T *item ) { handle_class< T > invalid_handle; if( item == NULL ) return invalid_handle; /* Ensure unique name. Optimized search for lowest unique name modifier */ if( file_name_to_handle_map[item->get_file_name()] ) { std::string original_name = item->get_file_name(); char *buf = new char[original_name.length() + 32]; int i; i = 2; while( 1 ) { sprintf( buf, "%s [%d]", original_name.c_str(), i ); if( file_name_to_handle_map.find( buf ) == file_name_to_handle_map.end() ) break; i *= 2; } int lower_bound = i / 2; int upper_bound = i; while( 1 ) { int halfway = upper_bound - lower_bound; if( halfway == 1 ) { i = upper_bound; break; } halfway /= 2; halfway += lower_bound; i = halfway; sprintf( buf, "%s [%d]", original_name.c_str(), i ); if( file_name_to_handle_map.find( buf ) == file_name_to_handle_map.end() ) upper_bound = halfway; // halfway is empty else lower_bound = halfway; // halfway is full } sprintf( buf, "%s [%d]", original_name.c_str(), i ); item->set_file_name( buf ); delete[] buf; } /* Insert handle to item in file_name_to_handle_map */ file_name_to_handle_map[item->get_file_name()] = item; /* Return handle to item */ return file_name_to_handle_map[item->get_file_name()]; } /* * Debug method. Log file names and handle pointer values in * file_name_to_handle_map. */ template< class T > inline void loader_class< T >::log_file_name_map( void ) { LOG << "Logging file name map for loader\n"; for( std::map< std::string, handle_class< T > >::iterator itor = file_name_to_handle_map.begin(); itor != file_name_to_handle_map.end(); ++itor ) { std::string file_name = itor->first; handle_class< T > handle = itor->second; LOGF( " file_name: %-30s => handle.ptr(): %p\n", file_name.c_str(), handle.ptr() ); } LOG << ENDLOG; } /* * Allocate a new item and add it to the file_name_to_handle_map at the * location specified by file_name. Then attempt to load it from the file * specified by file_name. */ template< class T > inline handle_class< T > loader_class< T >::load_item( const std::string &file_name, bool use_cached ) { handle_class< T > invalid_handle; /* Create a new item and set its file name to the requested name */ T *item = new T; item->set_file_name( file_name ); /* Add new item to the map. This can modify the item's file name */ add_item( item ); /* Store the possibly modified file name */ std::string item_name = item->get_file_name(); /* Set the item file name to the original requested file name */ item->set_file_name( file_name ); /* Load the item */ if( item->load( use_cached ) == false ) { POPUPLOG << "Failed to load item, name: " << item_name << ENDLOG; remove_item( item_name ); // item is automatically deleted return invalid_handle; } /* Restore the unique name assigned to the item by add_item method */ item->set_file_name( item_name ); /* Return a handle to the item */ return file_name_to_handle_map[item_name]; } /* * Erase the handle mapped to the given file_name. If it is the only * handle pointing to its item, that item will be automatically deleted as the * handle is deconstructed. */ template< class T > inline void loader_class< T >::remove_item( const std::string file_name ) { if( file_name_to_handle_map[file_name].valid() == true ) file_name_to_handle_map.erase( file_name ); else LOG << "Failed to remove item, file_name: " << file_name << ENDLOG; } #endif /* __loader_template_h__ */