ISIS Core Library 0.7.2 (api 3.0.0)
|
00001 // 00002 // C++ Implementation: io_factory 00003 // 00004 // Description: 00005 // 00006 // 00007 // Author: Enrico Reimer<reimer@cbs.mpg.de>, (C) 2009 00008 // 00009 // Copyright: See COPYING file that comes with this distribution 00010 // 00011 // 00012 00013 #include "io_factory.hpp" 00014 #ifdef WIN32 00015 #include <windows.h> 00016 #include <Winbase.h> 00017 #define BOOST_FILESYSTEM_VERSION 2 //@todo switch to 3 as soon as we drop support for boost < 1.44 00018 #include <boost/filesystem/path.hpp> 00019 #else 00020 #include <dlfcn.h> 00021 #endif 00022 #include <iostream> 00023 #include <vector> 00024 #include <algorithm> 00025 00026 #include "../CoreUtils/log.hpp" 00027 #include "common.hpp" 00028 #include <boost/regex.hpp> 00029 #include <boost/foreach.hpp> 00030 #include <boost/system/error_code.hpp> 00031 #include <boost/algorithm/string.hpp> 00032 #include "../CoreUtils/singletons.hpp" 00033 00034 namespace isis 00035 { 00036 namespace data 00037 { 00038 API_EXCLUDE_BEGIN 00040 namespace _internal 00041 { 00042 struct pluginDeleter { 00043 void *m_dlHandle; 00044 std::string m_pluginName; 00045 pluginDeleter( void *dlHandle, std::string pluginName ): m_dlHandle( dlHandle ), m_pluginName( pluginName ) {} 00046 void operator()( image_io::FileFormat *format ) { 00047 delete format; 00048 #ifdef WIN32 00049 00050 if( !FreeLibrary( ( HINSTANCE )m_dlHandle ) ) 00051 #else 00052 if ( dlclose( m_dlHandle ) != 0 ) 00053 #endif 00054 std::cerr << "Failed to release plugin " << m_pluginName << " (was loaded at " << m_dlHandle << ")"; 00055 00056 //we cannot use LOG here, because the loggers are gone allready 00057 } 00058 }; 00059 struct dialect_missing { 00060 util::istring dialect; 00061 std::string filename; 00062 bool operator()( IOFactory::FileFormatList::reference ref )const { 00063 const util::istring dia = ref->dialects( filename ); 00064 std::list<util::istring> splitted = util::stringToList<util::istring>( dia, ' ' ); 00065 const bool ret = ( std::find( splitted.begin(), splitted.end(), dialect ) == splitted.end() ); 00066 LOG_IF( ret, Runtime, warning ) << ref->getName() << " does not support the requested dialect " << util::MSubject( dialect ); 00067 return ret; 00068 } 00069 }; 00070 00071 bool invalid_and_tell( Chunk &candidate ) 00072 { 00073 LOG_IF( !candidate.isValid(), Runtime, error ) << "Ignoring invalid chunk. Missing properties: " << candidate.getMissing(); 00074 return !candidate.isValid(); 00075 } 00076 00077 } 00079 API_EXCLUDE_BEGIN 00080 00081 IOFactory::IOFactory() 00082 { 00083 const char *env_path = getenv( "ISIS_PLUGIN_PATH" ); 00084 const char *env_home = getenv( "HOME" ); 00085 00086 if( env_path ) { 00087 findPlugins( boost::filesystem::path( env_path ).directory_string() ); 00088 } 00089 00090 if( env_home ) { 00091 const boost::filesystem::path home = boost::filesystem::path( env_home ) / "isis" / "plugins"; 00092 00093 if( boost::filesystem::exists( home ) ) { 00094 findPlugins( home.directory_string() ); 00095 } else { 00096 LOG( Runtime, info ) << home.directory_string() << " does not exist. Won't check for plugins there"; 00097 } 00098 } 00099 00100 #ifdef WIN32 00101 TCHAR lpExeName[2048]; 00102 DWORD lExeName = GetModuleFileName( NULL, lpExeName, 2048 ); 00103 bool w32_path_ok = false; 00104 00105 if( lExeName == 0 ) { 00106 LOG( Runtime, error ) << "Failed to get the process name " << util::MSubject( util::getLastSystemError() ); 00107 } else if( lExeName < 2048 ) { 00108 lpExeName[lExeName] = '\0'; 00109 boost::filesystem::path prog_name( lpExeName ); 00110 00111 if( boost::filesystem::exists( prog_name ) ) { 00112 w32_path_ok = true; 00113 LOG( Runtime, info ) << "Determined the path of the executable as " << util::MSubject( prog_name.file_string() ) << " will search for plugins there.."; 00114 findPlugins( prog_name.remove_filename().directory_string() ); 00115 } 00116 } else 00117 LOG( Runtime, error ) << "Sorry, the path of the process is to long (must be less than 2048 characters) "; 00118 00119 LOG_IF( !w32_path_ok, Runtime, warning ) << "Could not determine the path of the executable, won't search for plugins there.."; 00120 #else 00121 findPlugins( std::string( PLUGIN_PATH ) ); 00122 #endif 00123 } 00124 00125 bool IOFactory::registerFileFormat( const FileFormatPtr plugin ) 00126 { 00127 if ( !plugin )return false; 00128 00129 io_formats.push_back( plugin ); 00130 std::list<util::istring> suffixes = plugin->getSuffixes( ); 00131 LOG( Runtime, info ) 00132 << "Registering " << ( plugin->tainted() ? "tainted " : "" ) << "io-plugin " 00133 << util::MSubject( plugin->getName() ) 00134 << " with supported suffixes " << suffixes; 00135 BOOST_FOREACH( util::istring & it, suffixes ) { 00136 io_suffix[it].push_back( plugin ); 00137 } 00138 return true; 00139 } 00140 00141 unsigned int IOFactory::findPlugins( const std::string &path ) 00142 { 00143 boost::filesystem::path p( path ); 00144 00145 if ( !exists( p ) ) { 00146 LOG( Runtime, warning ) << util::MSubject( p.file_string() ) << " not found"; 00147 return 0; 00148 } 00149 00150 if ( !boost::filesystem::is_directory( p ) ) { 00151 LOG( Runtime, warning ) << util::MSubject( p.file_string() ) << " is no directory"; 00152 return 0; 00153 } 00154 00155 LOG( Runtime, info ) << "Scanning " << util::MSubject( p ) << " for plugins"; 00156 boost::regex pluginFilter( std::string( "^" ) + DL_PREFIX + "isisImageFormat_" + "[[:word:]]+" + DL_SUFFIX + "$" ); 00157 unsigned int ret = 0; 00158 00159 for ( boost::filesystem::directory_iterator itr( p ); itr != boost::filesystem::directory_iterator(); ++itr ) { 00160 if ( boost::filesystem::is_directory( *itr ) )continue; 00161 00162 if ( boost::regex_match( itr->path().leaf(), pluginFilter ) ) { 00163 const std::string pluginName = itr->path().file_string(); 00164 #ifdef WIN32 00165 HINSTANCE handle = LoadLibrary( pluginName.c_str() ); 00166 #else 00167 void *handle = dlopen( pluginName.c_str(), RTLD_NOW ); 00168 #endif 00169 00170 if ( handle ) { 00171 #ifdef WIN32 00172 image_io::FileFormat* ( *factory_func )() = ( image_io::FileFormat * ( * )() )GetProcAddress( handle, "factory" ); 00173 #else 00174 image_io::FileFormat* ( *factory_func )() = ( image_io::FileFormat * ( * )() )dlsym( handle, "factory" ); 00175 #endif 00176 00177 if ( factory_func ) { 00178 FileFormatPtr io_class( factory_func(), _internal::pluginDeleter( handle, pluginName ) ); 00179 00180 if ( registerFileFormat( io_class ) ) { 00181 io_class->plugin_file = pluginName; 00182 ret++; 00183 } else { 00184 LOG( Runtime, warning ) << "failed to register plugin " << util::MSubject( pluginName ); 00185 } 00186 } else { 00187 #ifdef WIN32 00188 LOG( Runtime, warning ) 00189 << "could not get format factory function from " << util::MSubject( pluginName ); 00190 FreeLibrary( handle ); 00191 #else 00192 LOG( Runtime, warning ) 00193 << "could not get format factory function from " << util::MSubject( pluginName ) << ":" << util::MSubject( dlerror() ); 00194 dlclose( handle ); 00195 #endif 00196 } 00197 } else 00198 #ifdef WIN32 00199 LOG( Runtime, warning ) << "Could not load library " << util::MSubject( pluginName ); 00200 00201 #else 00202 LOG( Runtime, warning ) << "Could not load library " << util::MSubject( pluginName ) << ":" << util::MSubject( dlerror() ); 00203 #endif 00204 } else { 00205 LOG( Runtime, verbose_info ) << "Ignoring " << *itr << " because it doesn't match " << pluginFilter.str(); 00206 } 00207 } 00208 00209 return ret; 00210 } 00211 00212 IOFactory &IOFactory::get() 00213 { 00214 return util::Singletons::get<IOFactory, INT_MAX>(); 00215 } 00216 00217 size_t IOFactory::loadFile( std::list<Chunk> &ret, const boost::filesystem::path &filename, util::istring suffix_override, util::istring dialect ) 00218 { 00219 FileFormatList formatReader; 00220 formatReader = getFileFormatList( filename.file_string(), suffix_override, dialect ); 00221 const size_t nimgs_old = ret.size(); // save number of chunks 00222 const util::istring with_dialect = dialect.empty() ? 00223 util::istring( "" ) : util::istring( " with dialect \"" ) + dialect + "\""; 00224 00225 if ( formatReader.empty() ) { 00226 if( !boost::filesystem::exists( filename ) ) { 00227 LOG( Runtime, error ) << util::MSubject( filename.file_string() ) 00228 << " does not exist as file, and no suitable plugin was found to generate data from " 00229 << ( suffix_override.empty() ? util::istring( "that name" ) : util::istring( "the suffix \"" ) + suffix_override + "\"" ); 00230 } else if( suffix_override.empty() ) { 00231 LOG( Runtime, error ) << "No plugin found to read " << filename.file_string() << with_dialect; 00232 } else { 00233 LOG( Runtime, error ) << "No plugin supporting the requested suffix " << suffix_override << with_dialect << " was found"; 00234 } 00235 } else { 00236 BOOST_FOREACH( FileFormatList::const_reference it, formatReader ) { 00237 LOG( ImageIoDebug, info ) 00238 << "plugin to load file" << with_dialect << " " << util::MSubject( filename.file_string() ) << ": " << it->getName(); 00239 00240 try { 00241 return it->load( ret, filename.file_string(), dialect, m_feedback ); 00242 } catch ( std::runtime_error &e ) { 00243 if( suffix_override.empty() ) { 00244 LOG( Runtime, formatReader.size() > 1 ? warning : error ) 00245 << "Failed to load " << filename.file_string() << " using " << it->getName() << with_dialect << " ( " << e.what() << " )"; 00246 } else { 00247 LOG( Runtime, warning ) 00248 << "The enforced format " << it->getName() << " failed to read " << filename.file_string() << with_dialect 00249 << " ( " << e.what() << " ), maybe it just wasn't the right format"; 00250 } 00251 } 00252 } 00253 LOG_IF( boost::filesystem::exists( filename ) && formatReader.size() > 1, Runtime, error ) << "No plugin was able to load: " << util::MSubject( filename.file_string() ) << with_dialect; 00254 } 00255 00256 return ret.size() - nimgs_old;//no plugin of proposed list could load file 00257 } 00258 00259 00260 IOFactory::FileFormatList IOFactory::getFileFormatList( std::string filename, util::istring suffix_override, util::istring dialect ) 00261 { 00262 std::list<std::string> ext; 00263 FileFormatList ret; 00264 _internal::dialect_missing remove_op; 00265 00266 if( suffix_override.empty() ) { // detect suffixes from the filename 00267 const boost::filesystem::path fname( filename ); 00268 ext = util::stringToList<std::string>( fname.leaf(), '.' ); // get all suffixes 00269 00270 if( !ext.empty() )ext.pop_front(); // remove the first "suffix" - actually the basename 00271 } else ext = util::stringToList<std::string>( suffix_override, '.' ); 00272 00273 while( !ext.empty() ) { 00274 const util::istring wholeName( util::listToString( ext.begin(), ext.end(), ".", "", "" ).c_str() ); // (re)construct the rest of the suffix 00275 const std::map<util::istring, FileFormatList>::iterator found = get().io_suffix.find( wholeName ); 00276 00277 if( found != get().io_suffix.end() ) { 00278 LOG( Debug, verbose_info ) << found->second.size() << " plugins support suffix " << wholeName; 00279 ret.insert( ret.end(), found->second.begin(), found->second.end() ); 00280 } 00281 00282 ext.pop_front(); 00283 } 00284 00285 if( dialect.empty() ) { 00286 LOG_IF( ret.size() > 1, Debug, info ) << "No dialect given. Will use all " << ret.size() << " plugins"; 00287 } else {//remove everything which lacks the dialect if there was some given 00288 remove_op.dialect = dialect; 00289 remove_op.filename = filename; 00290 ret.remove_if( remove_op ); 00291 LOG( Debug, info ) << "Removed everything which does not support the dialect " << util::MSubject( dialect ) << " on " << filename << "(" << ret.size() << " plugins left)"; 00292 } 00293 00294 return ret; 00295 } 00296 00297 std::list< Image > IOFactory::chunkListToImageList( std::list<Chunk> &src ) 00298 { 00299 // throw away invalid chunks 00300 size_t errcnt = src.size(); 00301 src.remove_if( _internal::invalid_and_tell ); 00302 errcnt -= src.size(); 00303 00304 std::list< Image > ret; 00305 00306 while ( !src.empty() ) { 00307 LOG( Debug, info ) << src.size() << " Chunks left to be distributed."; 00308 size_t before = src.size(); 00309 00310 Image buff( src ); 00311 00312 if ( buff.isClean() ){ 00313 if( buff.isValid() ) { //if the image was successfully indexed and is valid, keep it 00314 ret.push_back( buff ); 00315 LOG( Runtime, info ) << "Image " << ret.size() << " with size " << util::MSubject(buff.getSizeAsString()) << " done."; 00316 } else { 00317 LOG_IF( !buff.getMissing().empty(), Runtime, error ) 00318 << "Cannot insert image. Missing properties: " << buff.getMissing(); 00319 errcnt += before - src.size(); 00320 } 00321 } else 00322 LOG(Runtime,info) << "Dropping non clean Image"; 00323 } 00324 00325 LOG_IF( errcnt, Runtime, warning ) << "Dropped " << errcnt << " chunks because they didn't form valid images"; 00326 return ret; 00327 } 00328 00329 size_t IOFactory::load( std::list<data::Chunk> &chunks, const std::string &path, util::istring suffix_override, util::istring dialect ) 00330 { 00331 const boost::filesystem::path p( path ); 00332 const size_t loaded = boost::filesystem::is_directory( p ) ? 00333 get().loadPath( chunks, p, suffix_override, dialect ) : 00334 get().loadFile( chunks, p, suffix_override, dialect ); 00335 BOOST_FOREACH( Chunk & ref, chunks ) { 00336 if ( ! ref.hasProperty( "source" ) ) 00337 ref.setPropertyAs( "source", p.file_string() ); 00338 } 00339 return loaded; 00340 } 00341 00342 std::list< Image > IOFactory::load ( const util::slist &paths, util::istring suffix_override, util::istring dialect ) 00343 { 00344 std::list<Chunk> chunks; 00345 size_t loaded = 0; 00346 BOOST_FOREACH( const std::string & path, paths ) { 00347 loaded += load( chunks, path , suffix_override, dialect ); 00348 } 00349 const std::list<data::Image> images = chunkListToImageList( chunks ); 00350 LOG( Runtime, info ) 00351 << "Generated " << images.size() << " images out of " << loaded << " chunks loaded from " << paths; 00352 return images; 00353 } 00354 00355 std::list<data::Image> IOFactory::load( const std::string &path, util::istring suffix_override, util::istring dialect ) 00356 { 00357 return load( util::slist( 1, path ), suffix_override, dialect ); 00358 } 00359 00360 size_t IOFactory::loadPath( std::list<Chunk> &ret, const boost::filesystem::path &path, util::istring suffix_override, util::istring dialect ) 00361 { 00362 int loaded = 0; 00363 00364 if( m_feedback ) { 00365 const size_t length = std::distance( boost::filesystem::directory_iterator( path ), boost::filesystem::directory_iterator() ); //@todo this will also count directories 00366 m_feedback->show( length, std::string( "Reading " ) + util::Value<std::string>( length ).toString( false ) + " files from " + path.file_string() ); 00367 } 00368 00369 for ( boost::filesystem::directory_iterator i( path ); i != boost::filesystem::directory_iterator(); ++i ) { 00370 if ( boost::filesystem::is_directory( *i ) )continue; 00371 00372 loaded += loadFile( ret, *i, suffix_override, dialect ); 00373 00374 if( m_feedback ) 00375 m_feedback->progress(); 00376 } 00377 00378 if( m_feedback ) 00379 m_feedback->close(); 00380 00381 return loaded; 00382 } 00383 00384 bool IOFactory::write( const data::Image &image, const std::string &path, util::istring suffix_override, util::istring dialect ) 00385 { 00386 return write( std::list<data::Image>( 1, image ), path, suffix_override, dialect ); 00387 } 00388 00389 00390 bool IOFactory::write( std::list< isis::data::Image > images, const std::string &path, util::istring suffix_override, util::istring dialect ) 00391 { 00392 const FileFormatList formatWriter = get().getFileFormatList( path, suffix_override, dialect ); 00393 00394 BOOST_FOREACH( std::list<data::Image>::reference ref, images ) { 00395 ref.checkMakeClean(); 00396 } 00397 00398 if( formatWriter.size() ) { 00399 BOOST_FOREACH( FileFormatList::const_reference it, formatWriter ) { 00400 LOG( Debug, info ) 00401 << "plugin to write to " << path << ": " << it->getName() 00402 << ( dialect.empty() ? 00403 util::istring( "" ) : 00404 util::istring( " using dialect: " ) + dialect 00405 ); 00406 00407 try { 00408 it->write( images, path, dialect, get().m_feedback ); 00409 LOG( Runtime, info ) 00410 << images.size() 00411 << " images written to " << path << " using " << it->getName() 00412 << ( dialect.empty() ? 00413 util::istring( "" ) : 00414 util::istring( " and dialect: " ) + dialect 00415 ); 00416 return true; 00417 } catch ( std::runtime_error &e ) { 00418 LOG( Runtime, warning ) 00419 << "Failed to write " << images.size() 00420 << " images to " << path << " using " << it->getName() << " (" << e.what() << ")"; 00421 } 00422 } 00423 } else { 00424 LOG( Runtime, error ) << "No plugin found to write to: " << path; //@todo error message missing 00425 } 00426 00427 return false; 00428 } 00429 void IOFactory::setProgressFeedback( boost::shared_ptr<util::ProgressFeedback> feedback ) 00430 { 00431 IOFactory &This = get(); 00432 00433 if( This.m_feedback )This.m_feedback->close(); 00434 00435 This.m_feedback = feedback; 00436 } 00437 00438 IOFactory::FileFormatList IOFactory::getFormats() 00439 { 00440 return get().io_formats; 00441 } 00442 00443 00444 } 00445 } // namespaces data isis