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