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