MOAB: Mesh Oriented datABase
(version 5.4.1)
|
00001 /* 00002 * MOAB, a Mesh-Oriented datABase, is a software component for creating, 00003 * storing and accessing finite element mesh data. 00004 * 00005 * Copyright 2004 Sandia Corporation. Under the terms of Contract 00006 * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government 00007 * retains certain rights in this software. 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 */ 00015 00016 /**\file FileOptions.cpp 00017 *\author Jason Kraftcheck ([email protected]) 00018 *\date 2007-08-21 00019 */ 00020 00021 #include "moab/FileOptions.hpp" 00022 00023 #include <cctype> 00024 #include <cstdlib> 00025 #include <cstring> 00026 #include <algorithm> 00027 00028 namespace moab 00029 { 00030 00031 const char DEFAULT_SEPARATOR = ';'; 00032 00033 static inline bool strempty( const char* s ) 00034 { 00035 return !*s; 00036 } 00037 00038 FileOptions::FileOptions( const char* str ) : mData( 0 ) 00039 { 00040 // if option string is null, just return 00041 if( !str ) return; 00042 00043 // check if alternate separator is specified 00044 char separator[2] = { DEFAULT_SEPARATOR, '\0' }; 00045 if( *str == DEFAULT_SEPARATOR ) 00046 { 00047 ++str; 00048 if( strempty( str ) ) return; 00049 separator[0] = *str; 00050 ++str; 00051 } 00052 00053 // don't bother allocating copy of input string if 00054 // input string is empty. 00055 if( !strempty( str ) ) 00056 { 00057 // tokenize at separator character 00058 mData = strdup( str ); 00059 for( char* i = strtok( mData, separator ); i; i = strtok( 0, separator ) ) 00060 if( !strempty( i ) ) // skip empty strings 00061 mOptions.push_back( i ); 00062 } 00063 00064 mSeen.resize( mOptions.size(), false ); 00065 } 00066 00067 FileOptions::FileOptions( const FileOptions& copy ) : mData( 0 ), mOptions( copy.mOptions.size() ) 00068 { 00069 if( !copy.mOptions.empty() ) 00070 { 00071 const char* last = copy.mOptions.back(); 00072 const char* endptr = last + strlen( last ) + 1; 00073 size_t len = endptr - copy.mData; 00074 mData = (char*)malloc( len ); 00075 memcpy( mData, copy.mData, len ); 00076 for( size_t i = 0; i < mOptions.size(); ++i ) 00077 mOptions[i] = mData + ( copy.mOptions[i] - copy.mData ); 00078 } 00079 mSeen = copy.mSeen; 00080 } 00081 00082 FileOptions& FileOptions::operator=( const FileOptions& copy ) 00083 { 00084 // Check for self-assignment 00085 if( this == © ) return *this; 00086 00087 free( mData ); 00088 mData = 0; 00089 mOptions.resize( copy.mOptions.size() ); 00090 00091 if( !copy.mOptions.empty() ) 00092 { 00093 const char* last = copy.mOptions.back(); 00094 const char* endptr = last + strlen( last ) + 1; 00095 size_t len = endptr - copy.mData; 00096 mData = (char*)malloc( len ); 00097 memcpy( mData, copy.mData, len ); 00098 for( size_t i = 0; i < mOptions.size(); ++i ) 00099 mOptions[i] = mData + ( copy.mOptions[i] - copy.mData ); 00100 } 00101 00102 mSeen = copy.mSeen; 00103 return *this; 00104 } 00105 00106 FileOptions::~FileOptions() 00107 { 00108 free( mData ); 00109 } 00110 00111 ErrorCode FileOptions::get_null_option( const char* name ) const 00112 { 00113 const char* s; 00114 ErrorCode rval = get_option( name, s ); 00115 if( MB_SUCCESS != rval ) return rval; 00116 return strempty( s ) ? MB_SUCCESS : MB_TYPE_OUT_OF_RANGE; 00117 } 00118 00119 ErrorCode FileOptions::get_int_option( const char* name, int& value ) const 00120 { 00121 const char* s; 00122 ErrorCode rval = get_option( name, s ); 00123 if( MB_SUCCESS != rval ) return rval; 00124 00125 // empty string 00126 if( strempty( s ) ) return MB_TYPE_OUT_OF_RANGE; 00127 00128 // parse value 00129 char* endptr; 00130 long int pval = strtol( s, &endptr, 0 ); 00131 if( !strempty( endptr ) ) // syntax error 00132 return MB_TYPE_OUT_OF_RANGE; 00133 00134 // check for overflow (parsing long int, returning int) 00135 value = pval; 00136 if( pval != (long int)value ) return MB_TYPE_OUT_OF_RANGE; 00137 00138 return MB_SUCCESS; 00139 } 00140 00141 ErrorCode FileOptions::get_int_option( const char* name, int default_val, int& value ) const 00142 { 00143 const char* s; 00144 ErrorCode rval = get_option( name, s ); 00145 if( MB_SUCCESS != rval ) return rval; 00146 00147 // empty string 00148 if( strempty( s ) ) 00149 { 00150 value = default_val; 00151 return MB_SUCCESS; 00152 } 00153 00154 // parse value 00155 char* endptr; 00156 long int pval = strtol( s, &endptr, 0 ); 00157 if( !strempty( endptr ) ) // syntax error 00158 return MB_TYPE_OUT_OF_RANGE; 00159 00160 // check for overflow (parsing long int, returning int) 00161 value = pval; 00162 if( pval != (long int)value ) return MB_TYPE_OUT_OF_RANGE; 00163 00164 return MB_SUCCESS; 00165 } 00166 00167 ErrorCode FileOptions::get_ints_option( const char* name, std::vector< int >& values ) const 00168 { 00169 const char* s; 00170 ErrorCode rval = get_option( name, s ); 00171 if( MB_SUCCESS != rval ) return rval; 00172 00173 // empty string 00174 if( strempty( s ) ) return MB_TYPE_OUT_OF_RANGE; 00175 00176 // parse values 00177 while( !strempty( s ) ) 00178 { 00179 char* endptr; 00180 long int sval = strtol( s, &endptr, 0 ); 00181 00182 #define EATSPACE( a ) \ 00183 while( ( *( a ) == ' ' || *( a ) == ',' ) && !strempty( a ) ) \ 00184 ( a )++; 00185 EATSPACE( endptr ); 00186 long int eval = sval; 00187 if( *endptr == '-' ) 00188 { 00189 endptr++; 00190 s = endptr; 00191 eval = strtol( s, &endptr, 0 ); 00192 EATSPACE( endptr ); 00193 } 00194 00195 // check for overflow (parsing long int, returning int) 00196 int value = sval; 00197 if( sval != (long int)value ) return MB_TYPE_OUT_OF_RANGE; 00198 value = eval; 00199 if( eval != (long int)value ) return MB_TYPE_OUT_OF_RANGE; 00200 00201 for( int i = sval; i <= eval; i++ ) 00202 values.push_back( i ); 00203 00204 s = endptr; 00205 } 00206 00207 return MB_SUCCESS; 00208 } 00209 00210 ErrorCode FileOptions::get_reals_option( const char* name, std::vector< double >& values ) const 00211 { 00212 const char* s; 00213 ErrorCode rval = get_option( name, s ); 00214 if( MB_SUCCESS != rval ) return rval; 00215 00216 // empty string 00217 if( strempty( s ) ) return MB_TYPE_OUT_OF_RANGE; 00218 00219 // parse values 00220 while( !strempty( s ) ) 00221 { 00222 char* endptr; 00223 double sval = strtod( s, &endptr ); 00224 00225 EATSPACE( endptr ); 00226 values.push_back( sval ); 00227 00228 s = endptr; 00229 } 00230 00231 return MB_SUCCESS; 00232 } 00233 00234 ErrorCode FileOptions::get_real_option( const char* name, double& value ) const 00235 { 00236 const char* s; 00237 ErrorCode rval = get_option( name, s ); 00238 if( MB_SUCCESS != rval ) return rval; 00239 00240 // empty string 00241 if( strempty( s ) ) return MB_TYPE_OUT_OF_RANGE; 00242 00243 // parse value 00244 char* endptr; 00245 value = strtod( s, &endptr ); 00246 if( !strempty( endptr ) ) // syntax error 00247 return MB_TYPE_OUT_OF_RANGE; 00248 00249 return MB_SUCCESS; 00250 } 00251 00252 ErrorCode FileOptions::get_strs_option( const char* name, std::vector< std::string >& values ) const 00253 { 00254 const char* s; 00255 ErrorCode rval = get_option( name, s ); 00256 if( MB_SUCCESS != rval ) return rval; 00257 00258 // empty string 00259 if( strempty( s ) ) return MB_TYPE_OUT_OF_RANGE; 00260 00261 // parse values 00262 char separator[3] = { ' ', ',', '\0' }; 00263 char* tmp_str = strdup( s ); 00264 for( char* i = strtok( tmp_str, separator ); i; i = strtok( 0, separator ) ) 00265 if( !strempty( i ) ) // skip empty strings 00266 values.push_back( std::string( i ) ); 00267 free( tmp_str ); 00268 00269 return MB_SUCCESS; 00270 } 00271 00272 ErrorCode FileOptions::get_str_option( const char* name, std::string& value ) const 00273 { 00274 const char* s; 00275 ErrorCode rval = get_option( name, s ); 00276 if( MB_SUCCESS != rval ) return rval; 00277 if( strempty( s ) ) return MB_TYPE_OUT_OF_RANGE; 00278 value = s; 00279 return MB_SUCCESS; 00280 } 00281 00282 ErrorCode FileOptions::get_option( const char* name, std::string& value ) const 00283 { 00284 const char* s; 00285 ErrorCode rval = get_option( name, s ); 00286 if( MB_SUCCESS != rval ) return rval; 00287 00288 value = s; 00289 return MB_SUCCESS; 00290 } 00291 00292 ErrorCode FileOptions::get_option( const char* name, const char*& value ) const 00293 { 00294 std::vector< const char* >::const_iterator i; 00295 for( i = mOptions.begin(); i != mOptions.end(); ++i ) 00296 { 00297 const char* opt = *i; 00298 if( compare( name, opt ) ) 00299 { 00300 value = opt + strlen( name ); 00301 // if compare returned true, next char after option 00302 // name must be either the null char or an equals symbol. 00303 if( *value == '=' ) ++value; 00304 00305 mSeen[i - mOptions.begin()] = true; 00306 return MB_SUCCESS; 00307 } 00308 } 00309 00310 return MB_ENTITY_NOT_FOUND; 00311 } 00312 00313 ErrorCode FileOptions::match_option( const char* name, const char* value ) const 00314 { 00315 int idx; 00316 const char* array[] = { value, NULL }; 00317 return match_option( name, array, idx ); 00318 } 00319 00320 ErrorCode FileOptions::match_option( const char* name, const char* const* values, int& index ) const 00321 { 00322 const char* optval; 00323 ErrorCode rval = get_option( name, optval ); 00324 if( MB_SUCCESS != rval ) return rval; 00325 00326 for( index = 0; values[index]; ++index ) 00327 if( compare( optval, values[index] ) ) return MB_SUCCESS; 00328 00329 index = -1; 00330 return MB_FAILURE; 00331 } 00332 00333 ErrorCode FileOptions::get_toggle_option( const char* name, bool default_value, bool& value ) const 00334 { 00335 static const char* values[] = { "true", "yes", "1", "on", "false", "no", "0", "off", 0 }; 00336 const int num_true = 4; 00337 00338 int index; 00339 ErrorCode result = match_option( name, values, index ); 00340 if( result == MB_SUCCESS ) 00341 { 00342 value = index < num_true; 00343 } 00344 else if( result == MB_ENTITY_NOT_FOUND ) 00345 { 00346 value = default_value; 00347 result = MB_SUCCESS; 00348 } 00349 else 00350 { 00351 result = MB_TYPE_OUT_OF_RANGE; 00352 } 00353 00354 return result; 00355 } 00356 00357 bool FileOptions::compare( const char* name, const char* option ) 00358 { 00359 while( !strempty( name ) && toupper( *name ) == toupper( *option ) ) 00360 { 00361 ++name; 00362 ++option; 00363 } 00364 // match if name matched option for length of name, 00365 // and option either matched entirely or matches up to 00366 // and equals sign. 00367 return strempty( name ) && ( strempty( option ) || *option == '=' ); 00368 } 00369 00370 void FileOptions::get_options( std::vector< std::string >& list ) const 00371 { 00372 list.clear(); 00373 list.resize( mOptions.size() ); 00374 std::copy( mOptions.begin(), mOptions.end(), list.begin() ); 00375 } 00376 00377 bool FileOptions::all_seen() const 00378 { 00379 return std::find( mSeen.begin(), mSeen.end(), false ) == mSeen.end(); 00380 } 00381 00382 void FileOptions::mark_all_seen() const 00383 { 00384 mSeen.clear(); 00385 mSeen.resize( mOptions.size(), true ); 00386 } 00387 00388 ErrorCode FileOptions::get_unseen_option( std::string& name ) const 00389 { 00390 std::vector< bool >::iterator i = std::find( mSeen.begin(), mSeen.end(), false ); 00391 if( i == mSeen.end() ) 00392 { 00393 name.clear(); 00394 return MB_ENTITY_NOT_FOUND; 00395 } 00396 00397 const char* opt = mOptions[i - mSeen.begin()]; 00398 const char* end = strchr( opt, '=' ); 00399 name = end ? std::string( opt, end - opt ) : std::string( opt ); 00400 return MB_SUCCESS; 00401 } 00402 00403 } // namespace moab 00404 00405 #ifdef TEST 00406 00407 using namespace moab; 00408 00409 #include <iostream> 00410 00411 #define CHECK( A ) \ 00412 if( MB_SUCCESS != ( A ) ) \ 00413 { \ 00414 std::cerr << "Failure at line " << __LINE__ << ": error code " << ( A ) << std::endl; \ 00415 return 1; \ 00416 } 00417 00418 #define EQUAL( A, B ) \ 00419 if( ( A ) != ( B ) ) \ 00420 { \ 00421 std::cerr << "Failure at line " << __LINE__ << ": expected " << ( B ) << " but got " << ( A ) << std::endl; \ 00422 return 2; \ 00423 } 00424 00425 int main() 00426 { 00427 FileOptions tool( "INT1=1;NUL1;STR1=ABC;DBL1=1.0;dbl2=2.0;DBL3=3.0;INT2=2;nul2;NUL3;INT3=3;str2=once upon a " 00428 "time;str3==fubar=;;INTS=1-3,5,6;DBLS=1.0,2.0, 3.0;STRS=var1, var2_var2;STRS2=" ); 00429 00430 std::string s; 00431 int i; 00432 double d; 00433 ErrorCode rval; 00434 00435 // test basic get_option method without deleting entry 00436 rval = tool.get_option( "STR1", s ); 00437 CHECK( rval ); 00438 EQUAL( s, "ABC" ); 00439 00440 // test basic get_option method again, this time deleting the entry 00441 rval = tool.get_option( "STR1", s ); 00442 CHECK( rval ); 00443 EQUAL( s, "ABC" ); 00444 00445 // test basig get_option method with a null option 00446 rval = tool.get_option( "NUL2", s ); 00447 CHECK( rval ); 00448 EQUAL( s.empty(), true ); 00449 00450 // test null option 00451 rval = tool.get_null_option( "nul1" ); 00452 CHECK( rval ); 00453 00454 // try null option method on non-null value 00455 rval = tool.get_null_option( "INT1" ); 00456 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00457 00458 // test integer option 00459 rval = tool.get_int_option( "int1", i ); 00460 CHECK( rval ); 00461 EQUAL( i, 1 ); 00462 00463 rval = tool.get_int_option( "int2", i ); 00464 CHECK( rval ); 00465 EQUAL( i, 2 ); 00466 00467 // test integer option on non-integer value 00468 rval = tool.get_int_option( "dbl2", i ); 00469 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00470 00471 // test integer option on null value 00472 rval = tool.get_int_option( "NUL3", i ); 00473 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00474 00475 // test double option 00476 rval = tool.get_real_option( "dbl1", d ); 00477 CHECK( rval ); 00478 EQUAL( d, 1.0 ); 00479 00480 rval = tool.get_real_option( "dbl2", d ); 00481 CHECK( rval ); 00482 EQUAL( d, 2.0 ); 00483 00484 rval = tool.get_real_option( "int3", d ); 00485 CHECK( rval ); 00486 EQUAL( d, 3.0 ); 00487 00488 // test real option on non-real value 00489 rval = tool.get_real_option( "str2", d ); 00490 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00491 00492 // test real option on null value 00493 rval = tool.get_real_option( "NUL3", d ); 00494 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00495 00496 // test get a simple string option 00497 rval = tool.get_str_option( "DBL3", s ); 00498 CHECK( rval ); 00499 EQUAL( s, "3.0" ); 00500 00501 // test get a string with spaces 00502 rval = tool.get_str_option( "STR2", s ); 00503 CHECK( rval ); 00504 EQUAL( s, "once upon a time" ); 00505 00506 // try to get a string value for a null option 00507 rval = tool.get_str_option( "nul3", s ); 00508 EQUAL( rval, MB_TYPE_OUT_OF_RANGE ); 00509 00510 // We haven't looked at all of the options yet 00511 EQUAL( false, tool.all_seen() ); 00512 rval = tool.get_unseen_option( s ); 00513 CHECK( rval ); 00514 EQUAL( s, "str3" ); 00515 00516 // test options using generic get_option method 00517 00518 rval = tool.get_option( "NUL3", s ); 00519 CHECK( rval ); 00520 EQUAL( s.empty(), true ); 00521 00522 rval = tool.get_option( "STR3", s ); 00523 CHECK( rval ); 00524 EQUAL( s, "=fubar=" ); 00525 00526 // test size of options string 00527 unsigned l = tool.size(); 00528 EQUAL( l, 16u ); 00529 00530 // test ints option 00531 std::vector< int > ivals; 00532 rval = tool.get_ints_option( "INTS", ivals ); 00533 CHECK( rval ); 00534 EQUAL( 5, ivals.size() ); 00535 EQUAL( 1, ivals[0] ); 00536 EQUAL( 2, ivals[1] ); 00537 EQUAL( 3, ivals[2] ); 00538 EQUAL( 5, ivals[3] ); 00539 EQUAL( 6, ivals[4] ); 00540 00541 // test dbls option 00542 std::vector< double > vals; 00543 rval = tool.get_reals_option( "DBLS", vals ); 00544 CHECK( rval ); 00545 EQUAL( 3, vals.size() ); 00546 EQUAL( 1.0, vals[0] ); 00547 EQUAL( 2.0, vals[1] ); 00548 EQUAL( 3.0, vals[2] ); 00549 00550 // test strs option 00551 std::vector< std::string > svals; 00552 rval = tool.get_strs_option( "STRS", svals ); 00553 CHECK( rval ); 00554 EQUAL( 2, svals.size() ); 00555 EQUAL( "var1", svals[0] ); 00556 EQUAL( "var2_var2", svals[1] ); 00557 00558 svals.clear(); 00559 rval = tool.get_strs_option( "STRS2", svals ); 00560 EQUAL( MB_TYPE_OUT_OF_RANGE, rval ); 00561 00562 // We requested every option 00563 EQUAL( true, tool.all_seen() ); 00564 rval = tool.get_unseen_option( s ); 00565 EQUAL( MB_ENTITY_NOT_FOUND, rval ); 00566 00567 // test alternate separator 00568 00569 FileOptions tool2( ";+OPT1=ABC+OPT2=" ); 00570 l = tool2.size(); 00571 EQUAL( l, 2 ); 00572 00573 // We haven't looked at all of the options yet 00574 EQUAL( false, tool2.all_seen() ); 00575 rval = tool2.get_unseen_option( s ); 00576 CHECK( rval ); 00577 EQUAL( s, "OPT1" ); 00578 00579 rval = tool2.get_option( "opt1", s ); 00580 CHECK( rval ); 00581 EQUAL( s, "ABC" ); 00582 00583 rval = tool2.get_option( "opt2", s ); 00584 CHECK( rval ); 00585 bool e = s.empty(); 00586 EQUAL( e, true ); 00587 00588 l = tool2.size(); 00589 EQUAL( l, 2 ); 00590 00591 // We requested every option 00592 EQUAL( true, tool2.all_seen() ); 00593 rval = tool2.get_unseen_option( s ); 00594 EQUAL( MB_ENTITY_NOT_FOUND, rval ); 00595 00596 // test empty options string 00597 00598 FileOptions tool3( ";;;;" ); 00599 e = tool3.empty(); 00600 EQUAL( e, true ); 00601 l = tool3.size(); 00602 EQUAL( l, 0 ); 00603 EQUAL( true, tool3.all_seen() ); 00604 00605 FileOptions tool4( NULL ); 00606 e = tool4.empty(); 00607 EQUAL( e, true ); 00608 l = tool4.size(); 00609 EQUAL( l, 0 ); 00610 EQUAL( true, tool4.all_seen() ); 00611 00612 FileOptions tool5( ";+" ); 00613 e = tool5.empty(); 00614 EQUAL( e, true ); 00615 l = tool5.size(); 00616 EQUAL( l, 0 ); 00617 EQUAL( true, tool5.all_seen() ); 00618 00619 // test copy constructor 00620 00621 const FileOptions& tool6( tool2 ); 00622 00623 rval = tool6.get_option( "opt1", s ); 00624 CHECK( rval ); 00625 EQUAL( s, "ABC" ); 00626 00627 rval = tool6.get_option( "opt2", s ); 00628 CHECK( rval ); 00629 e = s.empty(); 00630 EQUAL( e, true ); 00631 00632 l = tool6.size(); 00633 EQUAL( l, 2 ); 00634 00635 const FileOptions& tool7( tool5 ); 00636 e = tool7.empty(); 00637 EQUAL( e, true ); 00638 l = tool7.size(); 00639 EQUAL( l, 0 ); 00640 00641 // test assignment operator 00642 00643 FileOptions tool8( tool2 ); 00644 tool8 = tool; 00645 EQUAL( tool8.size(), tool.size() ); 00646 00647 return 0; 00648 } 00649 00650 #endif