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