ISIS Core Library 0.7.2 (api 3.0.0)
|
00001 #ifdef _MSC_VER 00002 #pragma warning(disable:4996) 00003 #endif 00004 00005 #include <boost/foreach.hpp> 00006 #define BOOST_FILESYSTEM_VERSION 2 //@todo switch to 3 as soon as we drop support for boost < 1.44 00007 #include <boost/filesystem.hpp> 00008 #include <iomanip> 00009 #include <iostream> 00010 00011 #include "../CoreUtils/log.hpp" 00012 #include "common.hpp" 00013 #include "io_interface.h" 00014 00015 namespace isis 00016 { 00017 namespace image_io 00018 { 00019 API_EXCLUDE_BEGIN 00021 namespace _internal 00022 { 00023 bool moreCmp( const util::istring &a, const util::istring &b ) {return a.length() > b.length();} 00024 } 00026 API_EXCLUDE_END 00027 00028 void FileFormat::write( const std::list< data::Image >& images, const std::string &filename, const util::istring &dialect, boost::shared_ptr< util::ProgressFeedback > progress ) throw( std::runtime_error & ) 00029 { 00030 std::list<std::string> names = makeUniqueFilenames( images, filename ); 00031 std::list<std::string>::const_iterator inames = names.begin(); 00032 BOOST_FOREACH( std::list<data::Image>::const_reference ref, images ) { 00033 std::string uniquePath = *( inames++ ); 00034 00035 try { 00036 write( ref, uniquePath, dialect, progress ); 00037 LOG( Runtime, notice ) 00038 << "Image of size " << ref.getSizeAsVector() << " written to " << uniquePath 00039 << " using " << getName() << ( dialect.empty() ? 00040 util::istring() : 00041 util::istring( " and dialect " ) + dialect 00042 ); 00043 } catch ( std::runtime_error &e ) { 00044 LOG( Runtime, warning ) 00045 << "Failed to write image to " << uniquePath << " using " << getName() << " (" << e.what() << ")"; 00046 } 00047 } 00048 } 00049 bool FileFormat::setGender( util::PropertyMap &object, const char *set, const char *entries ) 00050 { 00051 util::Selection g( entries ); 00052 00053 if( g.set( set ) ) { 00054 object.setPropertyAs( "subjectGender", g ); 00055 return true; 00056 } 00057 00058 return false; 00059 } 00060 00061 bool FileFormat::hasOrTell( const util::PropertyMap::KeyType &name, const util::PropertyMap &object, LogLevel level ) 00062 { 00063 if ( object.hasProperty( name ) ) { 00064 return true; 00065 } else { 00066 LOG( Runtime, level ) << "Missing property " << name; 00067 return false; 00068 } 00069 } 00070 00071 void FileFormat::throwGenericError( std::string desc ) 00072 { 00073 throw( std::runtime_error( desc ) ); 00074 } 00075 00076 void FileFormat::throwSystemError( int err, std::string desc ) 00077 { 00078 throw( boost::system::system_error( err, boost::system::get_system_category(), desc ) ); 00079 } 00080 00081 std::list< util::istring > FileFormat::getSuffixes( io_modes mode )const 00082 { 00083 std::list<util::istring> ret = util::stringToList<util::istring>( suffixes( mode ).c_str() ); 00084 BOOST_FOREACH( util::istring & ref, ret ) { 00085 ref.erase( 0, ref.find_first_not_of( '.' ) ); // remove leading . if there are some 00086 } 00087 ret.sort( _internal::moreCmp ); //start with the longest suffix 00088 return ret; 00089 } 00090 00091 std::pair< std::string, std::string > FileFormat::makeBasename( const std::string &filename )const 00092 { 00093 std::list<util::istring> supported_suffixes = getSuffixes(); 00094 util::istring ifilename( filename.begin(), filename.end() ); 00095 BOOST_FOREACH( const util::istring & suffix, supported_suffixes ) { 00096 util::istring check=ifilename.substr(ifilename.length()-suffix.length(),suffix.length()); 00097 00098 if(filename[filename.length()-suffix.length()-1]=='.' && check == suffix ) { 00099 return std::make_pair( filename.substr( 0, filename.length()-suffix.length()-1 ), filename.substr( filename.length()-suffix.length()-1 ) ); 00100 } 00101 } 00102 return std::make_pair( filename, std::string() ); 00103 } 00104 00105 std::string FileFormat::makeFilename( const util::PropertyMap &props, std::string namePattern ) 00106 { 00107 boost::regex reg( "\\{[^{}]+\\}" ); 00108 boost::regex regFormatInt( "%d_" ); // add leading zeros to int values - always as much as possible 00109 //NOTE: can also be done for rounding floats, but at the moment not required so not done right now 00110 00111 boost::match_results<std::string::iterator> what; 00112 std::string::iterator pos = namePattern.begin(); 00113 00114 00115 while( boost::regex_search( pos, namePattern.end() , what, reg ) ) { 00116 00117 bool isFormatUsed = false; 00118 boost::cmatch m; 00119 size_t mSize = 1; 00120 00121 if ( boost::regex_match( what[0].str().substr( 1, regFormatInt.size() ).c_str(), m, regFormatInt ) ) { 00122 mSize += regFormatInt.size(); 00123 isFormatUsed = true; 00124 } 00125 00126 util::PropertyMap::KeyType prop( what[0].str().substr( mSize, what.length() - 1 - mSize ).c_str() ); 00127 const std::string::iterator start = what[0].first, end = what[0].second; 00128 00129 if( props.hasProperty( prop ) ) { 00130 std::string pstring; 00131 00132 if ( true == isFormatUsed ) { 00133 size_t overallDigits = 0; 00134 unsigned short tID; 00135 00136 switch ( tID = ( *props.propertyValue( prop ) ).getTypeID() ) { 00137 case util::Value<uint8_t>::staticID: 00138 overallDigits = ceil( log10( std::numeric_limits<uint8_t>::max() ) ); 00139 break; 00140 case util::Value<int8_t>::staticID: 00141 overallDigits = ceil( log10( std::numeric_limits<int8_t>::max() ) ); 00142 break; 00143 case util::Value<uint16_t>::staticID: 00144 overallDigits = ceil( log10( std::numeric_limits<uint16_t>::max() ) ); 00145 break; 00146 case util::Value<int16_t>::staticID: 00147 overallDigits = ceil( log10( std::numeric_limits<int16_t>::max() ) ); 00148 break; 00149 case util::Value<uint32_t>::staticID: 00150 overallDigits = ceil( log10( std::numeric_limits<uint32_t>::max() ) ); 00151 break; 00152 case util::Value<int32_t>::staticID: 00153 overallDigits = ceil( log10( std::numeric_limits<int32_t>::max() ) ); 00154 break; 00155 default: 00156 break; 00157 } 00158 00159 pstring = boost::regex_replace( props.getPropertyAs<std::string>( prop ), boost::regex( "[[:space:]/\\\\]" ), "_" ); 00160 00161 if ( 0 < overallDigits ) { 00162 size_t zerosToFill = overallDigits - pstring.length(); 00163 pstring.insert( 0, zerosToFill, '0' ); 00164 } 00165 } else { 00166 pstring = boost::regex_replace( props.getPropertyAs<std::string>( prop ), boost::regex( "[[:space:]/\\\\]" ), "_" ); 00167 } 00168 00169 const size_t dist = start - namePattern.begin(); 00170 00171 namePattern.replace( start, end, pstring ); 00172 00173 pos = namePattern.begin() + dist + pstring.length(); 00174 00175 LOG( Debug, info ) 00176 << "Replacing " << util::PropertyMap::KeyType( "{" ) + prop + "}" << " by " << props.getPropertyAs<std::string>( prop ) 00177 << " the string is now " << namePattern; 00178 } else { 00179 LOG( Runtime, warning ) << "The property " << util::MSubject( prop ) << " does not exist - ignoring it"; 00180 namePattern.replace( start, end, "" ); // it must be removed, or it will match forever 00181 } 00182 } 00183 00184 return namePattern; 00185 } 00186 00187 std::list<std::string> FileFormat::makeUniqueFilenames( const std::list<data::Image> &images, const std::string &namePattern )const 00188 { 00189 std::list<std::string> ret; 00190 std::map<std::string, unsigned short> names, used_names; 00191 BOOST_FOREACH( std::list<data::Image>::const_reference ref, images ) { 00192 names[makeFilename( ref, namePattern )]++; 00193 } 00194 00195 BOOST_FOREACH( std::list<data::Image>::const_reference ref, images ) { 00196 std::string name = makeFilename( ref, namePattern ); 00197 00198 if( names[name] > 1 ) { 00199 const unsigned short number = ++used_names[name]; 00200 const unsigned short length = ( uint16_t )log10( ( float )names[name] ) - ( uint16_t )log10( ( float )number ); 00201 const std::string snumber = std::string( length, '0' ) + boost::lexical_cast<std::string>( number ); 00202 const std::pair<std::string, std::string> splitted = makeBasename( name ); 00203 name = splitted.first + "_" + snumber + splitted.second; 00204 } 00205 00206 ret.push_back( name ); 00207 } 00208 return ret; 00209 } 00210 00211 const float FileFormat::invalid_float = -std::numeric_limits<float>::infinity(); 00212 } 00213 }