ISIS Core Library 0.7.2 (api 3.0.0)

/scr/tee1/isis/lib/Core/DataStorage/io_factory.cpp

Go to the documentation of this file.
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