contrib/mul/mbl/mbl_read_props.cxx
Go to the documentation of this file.
00001 // This is mul/mbl/mbl_read_props.cxx
00002 //:
00003 // \file
00004 
00005 #include "mbl_read_props.h"
00006 #include <vsl/vsl_indent.h>
00007 #include <vcl_sstream.h>
00008 #include <vcl_iostream.h>
00009 #include <vcl_string.h>
00010 #include <vcl_cctype.h>
00011 #include <vcl_utility.h>
00012 
00013 #include <mbl/mbl_parse_block.h>
00014 #include <mbl/mbl_exception.h>
00015 
00016 
00017 // Return the contents for a given (required) property prop.
00018 // prop is removed from the property list.
00019 // \throws mbl_exception_missing_property if prop doesn't exist
00020 vcl_string mbl_read_props_type::get_required_property(const vcl_string &prop)
00021 {
00022   mbl_read_props_type::iterator it = this->find(prop);
00023   if (it==this->end()) 
00024     mbl_exception_error(mbl_exception_missing_property(prop));
00025   vcl_string result = it->second;
00026   this->erase(it);
00027   return result;
00028 }
00029 
00030 
00031 // Return the contents for a given (optional) property prop.
00032 // prop is removed from the property list.
00033 // Returns empty string if prop doesn't exist.
00034 vcl_string mbl_read_props_type::get_optional_property(const vcl_string &prop,
00035                                                       const vcl_string &def_value /*=""*/)
00036 {
00037   vcl_string result(def_value);
00038   mbl_read_props_type::iterator it = this->find(prop);
00039   if (it!=this->end()) 
00040   {
00041     result = it->second;
00042     this->erase(it);
00043   }
00044   return result;  
00045 }
00046 
00047 
00048 void mbl_read_props_print(vcl_ostream &afs, mbl_read_props_type props)
00049 {
00050   typedef vcl_map<vcl_string, vcl_string>::iterator ITER;
00051   afs << vsl_indent() << "{\n";
00052   vsl_indent_inc(afs);
00053   for (ITER i = props.begin(); i != props.end(); ++i)
00054     afs << vsl_indent() << (*i).first << ": " << (*i).second << '\n';
00055   vsl_indent_dec(afs);
00056   afs << vsl_indent() << "}\n";
00057 }
00058 
00059 
00060 void mbl_read_props_print(vcl_ostream &afs, mbl_read_props_type props, unsigned max_chars)
00061 {
00062   typedef vcl_map<vcl_string, vcl_string>::iterator ITER;
00063   afs << vsl_indent() << "{\n";
00064   vsl_indent_inc(afs);
00065   for (ITER i = props.begin(); i != props.end(); ++i)
00066   {
00067     afs << vsl_indent() << (*i).first << ": " << (*i).second.substr(0, max_chars) << '\n';
00068     if (max_chars < (*i).second.size()) afs << vsl_indent() << "...\n";
00069   }
00070   vsl_indent_dec(afs);
00071   afs << vsl_indent() << "}\n";
00072 }
00073 
00074 
00075 static void strip_trailing_ws(vcl_string &s)
00076 {
00077   int p=s.length()-1;
00078   while (p>0 && vcl_isspace(s[p])) --p;
00079   s.erase(p+1);
00080 }
00081 
00082 //: Read properties from a text stream.
00083 // The function will terminate on an eof. If one of
00084 // the opening lines contains an opening brace '{', then the function
00085 // will also stop reading the stream after finding a line containing
00086 // a closing brace '}'
00087 //
00088 // There should be one property per line, with a colon ':' after
00089 // each property label. The remainder of that line is the property value.
00090 // If the next line begins with a brace, the following text up to matching
00091 // braces is included in the property value.
00092 // Each property label should not contain
00093 // any whitespace.
00094 mbl_read_props_type mbl_read_props(vcl_istream &afs)
00095 {
00096   if (!afs) return mbl_read_props_type();
00097 
00098   vcl_string label, str1;
00099 
00100   while ( afs>>vcl_ws, !afs.eof() )
00101   {
00102     afs >> label;
00103     if (label.substr(0,2) =="//")
00104     {
00105       // Comment line, so read to end
00106       vcl_getline( afs, str1 );
00107     }
00108     else break;
00109   }
00110 
00111   bool need_closing_brace = false;
00112 
00113   if (label[0] == '{')
00114   {
00115     need_closing_brace = true;
00116     label.erase(0,1);
00117   }
00118 
00119   mbl_read_props_type props;
00120 
00121   if ( label.empty() )
00122   {
00123     afs >> vcl_ws;
00124 
00125     // Several tests with Borland 5.5.1 fail because this next
00126     // statement 'afs >> label;' moves past the '\n' char when the
00127     // next section of the stream looks like "//comment\n a: a".  With
00128     // Borland 5.5.1, after this statement, afs.peek() returns 32
00129     // (space), while other compilers it returns 10 ('\n').  Seems
00130     // like a Borland standard library problem.  -Fred Wheeler
00131 
00132     afs >> label;
00133 
00134     // vcl_cout << "debug label " << label << vcl_endl
00135     //          << "debug peek() " << afs.peek() << vcl_endl;
00136   }
00137 
00138   vcl_string last_label( label );
00139 
00140   do
00141   {
00142     if ( label.substr(0,2) =="//" )
00143     {
00144       // Comment line, so read to end
00145       vcl_getline(afs, str1);
00146     }
00147     else if ( need_closing_brace && label[0] == '}' )
00148     {
00149       // Strip rest of line
00150       return props;
00151     }
00152     else if ( !label.empty() )
00153     {
00154       if ( label.size() > 1 &&
00155            label[label.size() -1] == ':' )
00156       {
00157         label.erase( label.size() -1, 1 );
00158         afs >> vcl_ws;
00159         vcl_getline(afs, str1);
00160 
00161         if ( str1.substr(0,1) == "{" )
00162         {
00163           str1 = mbl_parse_block(afs, true);
00164         }
00165 
00166         strip_trailing_ws(str1);
00167 
00168         mbl_read_props_type::iterator it = props.lower_bound(label);
00169 
00170         if (it != props.end() && it->first == label)
00171         {
00172           mbl_exception_warning(
00173             mbl_exception_read_props_parse_error( vcl_string(
00174               "Found second entry with label \"") + label + '"' ) );
00175           afs.clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00176           return props;
00177         }
00178 
00179 
00180         props.insert(it, vcl_make_pair(label, str1));
00181         last_label = label;
00182       }
00183       else if ( label.substr(0,1) == "{" )
00184       {
00185         vcl_string block = mbl_parse_block( afs, true );
00186         if ( block.substr(0,2) != "{}" )
00187         {
00188           vcl_string prop = props[ last_label ];
00189           prop += "\n";
00190           prop += block;
00191           props[ last_label ] = prop;
00192         }
00193       }
00194       else
00195       {
00196         char c;
00197         afs >> vcl_ws;
00198         afs >> c;
00199 
00200         if (c != ':')
00201         {
00202           vcl_getline(afs, str1);
00203           // The next loop replaces any characters outside the ASCII range
00204           // 32-126 with their XML equivalent, e.g. a TAB with &#9;
00205           // This is necessary for the tests dashboard since otherwise the
00206           // the Dart server gives up on interpreting the XML file sent. - PVr
00207           for (int i=-1; i<256; ++i)
00208           {
00209             char c= i<0 ? '&' : char(i); vcl_string s(1,c); // first do '&'
00210             if (i>=32 && i<127 && c!='<')
00211               continue; // keep "normal" chars
00212 
00213             vcl_ostringstream os; os << "&#" << (i<0?int(c):i) << ';';
00214             vcl_string::size_type pos;
00215             
00216             while ((pos=str1.find(s)) != vcl_string::npos)
00217               str1.replace(pos,1,os.str());
00218 
00219             while ((pos=label.find(s)) != vcl_string::npos)
00220               label.replace(pos,1,os.str());
00221           }
00222           mbl_exception_warning(
00223             mbl_exception_read_props_parse_error( vcl_string(
00224               "Could not find colon ':' separator while reading line ")
00225               + label + " " + str1) );
00226           afs.clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00227           return props;
00228         }
00229       }
00230     }
00231 
00232     afs >> vcl_ws >> label;
00233   }
00234 
00235   while ( !afs.eof() );
00236 
00237   if ( need_closing_brace && label != "}" )
00238   {
00239     mbl_exception_warning(
00240       mbl_exception_read_props_parse_error( vcl_string(
00241         "Unexpected end of file while "
00242         "looking for '}'. Last read string = \"")
00243         + label +'"') );
00244     afs.clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00245   }
00246 
00247   return props;
00248 }
00249 
00250 
00251 
00252 //: Read properties from a text stream.
00253 // The function will terminate on an eof. If one of
00254 // the opening lines contains an opening brace '{', then the function
00255 // will also stop reading the stream after finding a line containing
00256 // a closing brace '}'
00257 //
00258 // Every property label ends in ":", and should not contain
00259 // any whitespace.
00260 // Differs from mbl_read_props(afs) in that all whitespace is treated
00261 // as a separator.
00262 // If there is a brace after the first string following the label,
00263 // the following text up to matching
00264 // braces is included in the property value.
00265 // Each property label should not contain
00266 // any whitespace.
00267 mbl_read_props_type mbl_read_props_ws(vcl_istream &afs)
00268 {
00269   if (!afs) return mbl_read_props_type();
00270 
00271   vcl_string label, str1;
00272 
00273   while ( afs>>vcl_ws, !afs.eof() )
00274   {
00275     afs >> label;
00276     if (label.substr(0,2) =="//")
00277     {
00278       // Comment line, so read to end
00279       vcl_getline( afs, str1 );
00280     }
00281     else break;
00282   }
00283 
00284   if (afs.eof()) return mbl_read_props_type();
00285 
00286   bool need_closing_brace = false;
00287 
00288   if (label[0] == '{')
00289   {
00290     need_closing_brace = true;
00291     label.erase(0,1);
00292   }
00293 
00294   mbl_read_props_type props;
00295 
00296   if ( label.empty() )
00297   {
00298     afs >> vcl_ws;
00299 
00300     // Several tests with Borland 5.5.1 fail because this next
00301     // statement 'afs >> label;' moves past the '\n' char when the
00302     // next section of the stream looks like "//comment\n a: a".  With
00303     // Borland 5.5.1, after this statement, afs.peek() returns 32
00304     // (space), while other compilers it returns 10 ('\n').  Seems
00305     // like a Borland standard library problem.  -Fred Wheeler
00306 
00307     afs >> label;
00308 
00309     // vcl_cout << "debug label " << label << vcl_endl
00310     //          << "debug peek() " << afs.peek() << vcl_endl;
00311   }
00312 
00313   vcl_string last_label( label );
00314 
00315   do
00316   {
00317     if ( label.substr(0,2) =="//" )
00318     {
00319       // Comment line, so read to end
00320       vcl_getline(afs, str1);
00321     }
00322     else if ( need_closing_brace && label[0] == '}' )
00323     {
00324       // Strip rest of line
00325       return props;
00326     }
00327     else if ( !label.empty() )
00328     {
00329       if ( label.size() > 1 &&
00330            label[label.size() -1] == ':' )
00331       {
00332         label.erase( label.size() -1, 1 );
00333 
00334         char brace;
00335         afs >> vcl_ws >> brace;
00336 
00337         if (brace == '{')
00338           str1 = mbl_parse_block(afs, true);
00339         else
00340         {
00341           afs.putback(brace);
00342           afs >> str1;
00343         }
00344 
00345 
00346         strip_trailing_ws(str1);
00347 
00348         mbl_read_props_type::iterator it = props.lower_bound(label);
00349 
00350         if (it != props.end() && it->first == label)
00351         {
00352           mbl_exception_warning(
00353             mbl_exception_read_props_parse_error( vcl_string(
00354               "Found second entry with label \"") + label + '"' ) );
00355           afs.clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00356           return props;
00357         }
00358 
00359         props.insert(it, vcl_make_pair(label, str1));
00360 
00361         last_label = label;
00362       }
00363       else if ( label.substr(0,1) == "{" )
00364       {
00365         vcl_string block = mbl_parse_block( afs, true );
00366         if ( block.substr(0,2) != "{}" )
00367         {
00368           vcl_string prop = props[ last_label ];
00369           prop += " ";
00370           prop += block;
00371           props[ last_label ] = prop;
00372         }
00373       }
00374       else
00375       {
00376         char c;
00377         afs >> vcl_ws;
00378         afs >> c;
00379 
00380         if (c != ':')
00381         {
00382           vcl_getline(afs, str1);
00383           // The next loop replaces any characters outside the ASCII range
00384           // 32-126 with their XML equivalent, e.g. a TAB with &#9;
00385           // This is necessary for the tests dashboard since otherwise the
00386           // the Dart server gives up on interpreting the XML file sent. - PVr
00387           for (int i=-1; i<256; ++i)
00388           {
00389             char c= i<0 ? '&' : char(i); vcl_string s(1,c); // first do '&'
00390             if (i>=32 && i<127 && c!='<')
00391               continue; // keep "normal" chars
00392 
00393             vcl_ostringstream os; os << "&#" << (i<0?int(c):i) << ';';
00394             vcl_string::size_type pos;
00395             
00396             while ((pos=str1.find(s)) != vcl_string::npos)
00397               str1.replace(pos,1,os.str());
00398 
00399             while ((pos=label.find(s)) != vcl_string::npos)
00400               label.replace(pos,1,os.str());
00401           }
00402           mbl_exception_warning(
00403             mbl_exception_read_props_parse_error( vcl_string(
00404               "Could not find colon ':' separator while reading line ")
00405               + label + " " + str1) );
00406           afs.clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00407           return props;
00408         }
00409       }
00410     }
00411 
00412     afs >> vcl_ws >> label;
00413   }
00414 
00415   while ( !afs.eof() );
00416 
00417   if ( need_closing_brace && label != "}" )
00418   {
00419     mbl_exception_warning(
00420       mbl_exception_read_props_parse_error( vcl_string(
00421         "Unexpected end of file while "
00422         "looking for '}'. Last read string = \"")
00423         + label + '"') );
00424     afs.clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00425     return props;
00426   }
00427 
00428   return props;
00429 }
00430 
00431 
00432 //: merge two property sets.
00433 // \param first_overrides
00434 // properties in "a" will override identically named properties in "b"
00435 mbl_read_props_type mbl_read_props_merge(const mbl_read_props_type& a,
00436                                          const mbl_read_props_type& b,
00437                                          bool first_overrides/*=true*/)
00438 {
00439   mbl_read_props_type output;
00440 
00441   mbl_read_props_type::const_iterator a_it = a.begin();
00442   mbl_read_props_type::const_iterator b_it = b.begin();
00443 
00444 
00445   while (a_it != a.end() || b_it != b.end())
00446   {
00447     if (a_it == a.end())
00448       output.insert(*(b_it++));
00449     else if (b_it == b.end())
00450       output.insert(*(a_it++));
00451     else if (a_it->first < b_it->first)
00452       output.insert(*(a_it++));
00453     else if (a_it->first > b_it->first)
00454       output.insert(*(b_it++));
00455     else
00456     {
00457       if (first_overrides)
00458         output.insert(*a_it);
00459       else
00460         output.insert(*b_it);
00461       ++a_it; ++b_it;
00462     }
00463   }
00464   return output;
00465 }
00466 
00467 //: Throw error if there are any keys in props that aren't in ignore.
00468 // \throw mbl_exception_unused_props
00469 void mbl_read_props_look_for_unused_props(
00470   const vcl_string & function_name,
00471   const mbl_read_props_type &props,
00472   const mbl_read_props_type &ignore)
00473 {
00474   mbl_read_props_type p2(props);
00475   
00476   // Remove ignoreable properties
00477   for (mbl_read_props_type::const_iterator it=ignore.begin();
00478          it != ignore.end(); ++it)
00479     p2.erase(it->first);
00480 
00481   if (!p2.empty())
00482   {
00483 
00484     vcl_ostringstream ss;
00485     mbl_read_props_print(ss, p2, 1000);
00486     mbl_exception_error(mbl_exception_unused_props(function_name, ss.str()));
00487   }
00488 }
00489