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