Section readers moved to distinct class
authorBalazs Dezso <deba@inf.elte.hu>
Fri, 04 Jul 2008 15:21:48 +0200
changeset 189a63ed81c57ba
parent 188 70694e6bdcac
child 190 1e6af6f0843c
Section readers moved to distinct class
lemon/lgf_reader.h
     1.1 --- a/lemon/lgf_reader.h	Thu Jul 03 12:40:04 2008 +0200
     1.2 +++ b/lemon/lgf_reader.h	Fri Jul 04 15:21:48 2008 +0200
     1.3 @@ -419,9 +419,7 @@
     1.4    /// By default the reader uses the first section in the file of the
     1.5    /// proper type. If a section has an optional name, then it can be
     1.6    /// selected for reading by giving an optional name parameter to the
     1.7 -  /// \c nodes(), \c arcs() or \c attributes() functions. The readers
     1.8 -  /// also can load extra sections with the \c sectionLines() and
     1.9 -  /// sectionStream() functions.
    1.10 +  /// \c nodes(), \c arcs() or \c attributes() functions.
    1.11    ///
    1.12    /// The \c useNodes() and \c useArcs() functions are used to tell the reader
    1.13    /// that the nodes or arcs should not be constructed (added to the
    1.14 @@ -473,9 +471,6 @@
    1.15        Attributes;
    1.16      Attributes _attributes;
    1.17  
    1.18 -    typedef std::map<std::string, _reader_bits::Section*> Sections;
    1.19 -    Sections _sections;
    1.20 -
    1.21      bool _use_nodes;
    1.22      bool _use_arcs;
    1.23  
    1.24 @@ -537,7 +532,6 @@
    1.25        _arcs_caption = other._arcs_caption;
    1.26        _attributes_caption = other._attributes_caption;
    1.27  
    1.28 -      _sections.swap(other._sections);
    1.29      }
    1.30  
    1.31      /// \brief Destructor
    1.32 @@ -557,11 +551,6 @@
    1.33  	delete it->second;
    1.34        }
    1.35  
    1.36 -      for (typename Sections::iterator it = _sections.begin(); 
    1.37 -	   it != _sections.end(); ++it) {
    1.38 -	delete it->second;
    1.39 -      }
    1.40 -
    1.41        if (local_is) {
    1.42  	delete _is;
    1.43        }
    1.44 @@ -708,83 +697,6 @@
    1.45  
    1.46      /// @}
    1.47  
    1.48 -    /// \name Section readers
    1.49 -    /// @{
    1.50 -
    1.51 -    /// \brief Add a section processor with line oriented reading
    1.52 -    ///
    1.53 -    /// In the \e LGF file extra sections can be placed, which contain
    1.54 -    /// any data in arbitrary format. These sections can be read with
    1.55 -    /// this function line by line. The first parameter is the type
    1.56 -    /// descriptor of the section, the second is a functor, which
    1.57 -    /// takes just one \c std::string parameter. At the reading
    1.58 -    /// process, each line of the section will be given to the functor
    1.59 -    /// object. However, the empty lines and the comment lines are
    1.60 -    /// filtered out, and the leading whitespaces are stipped from
    1.61 -    /// each processed string.
    1.62 -    ///
    1.63 -    /// For example let's see a section, which contain several
    1.64 -    /// integers, which should be inserted into a vector.
    1.65 -    ///\code
    1.66 -    ///  @numbers
    1.67 -    ///  12 45 23
    1.68 -    ///  4
    1.69 -    ///  23 6
    1.70 -    ///\endcode
    1.71 -    ///
    1.72 -    /// The functor is implemented as an struct:
    1.73 -    ///\code
    1.74 -    ///  struct NumberSection {
    1.75 -    ///    std::vector<int>& _data;
    1.76 -    ///    NumberSection(std::vector<int>& data) : _data(data) {}
    1.77 -    ///    void operator()(const std::string& line) {
    1.78 -    ///      std::istringstream ls(line);
    1.79 -    ///      int value;
    1.80 -    ///      while (ls >> value) _data.push_back(value);
    1.81 -    ///    }
    1.82 -    ///  };
    1.83 -    ///
    1.84 -    ///  // ...
    1.85 -    ///
    1.86 -    ///  reader.sectionLines("numbers", NumberSection(vec));  
    1.87 -    ///\endcode
    1.88 -    template <typename Functor>
    1.89 -    DigraphReader& sectionLines(const std::string& type, Functor functor) {
    1.90 -      LEMON_ASSERT(!type.empty(), "Type is not empty.");
    1.91 -      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
    1.92 -		   "Multiple reading of section.");
    1.93 -      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
    1.94 -		   type != "attributes", "Multiple reading of section.");
    1.95 -      _sections.insert(std::make_pair(type, 
    1.96 -        new _reader_bits::LineSection<Functor>(functor)));
    1.97 -      return *this;
    1.98 -    }
    1.99 -
   1.100 -
   1.101 -    /// \brief Add a section processor with stream oriented reading
   1.102 -    ///
   1.103 -    /// In the \e LGF file extra sections can be placed, which contain
   1.104 -    /// any data in arbitrary format. These sections can be read
   1.105 -    /// directly with this function. The first parameter is the type
   1.106 -    /// of the section, the second is a functor, which takes an \c
   1.107 -    /// std::istream& and an int& parameter, the latter regard to the
   1.108 -    /// line number of stream. The functor can read the input while
   1.109 -    /// the section go on, and the line number should be modified
   1.110 -    /// accordingly.
   1.111 -    template <typename Functor>
   1.112 -    DigraphReader& sectionStream(const std::string& type, Functor functor) {
   1.113 -      LEMON_ASSERT(!type.empty(), "Type is not empty.");
   1.114 -      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
   1.115 -		   "Multiple reading of section.");
   1.116 -      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
   1.117 -		   type != "attributes", "Multiple reading of section.");
   1.118 -      _sections.insert(std::make_pair(type, 
   1.119 -	 new _reader_bits::StreamSection<Functor>(functor)));
   1.120 -      return *this;
   1.121 -    }    
   1.122 -    
   1.123 -    /// @}
   1.124 -
   1.125      /// \name Using previously constructed node or arc set
   1.126      /// @{
   1.127  
   1.128 @@ -1188,7 +1100,6 @@
   1.129        bool nodes_done = _skip_nodes;
   1.130        bool arcs_done = _skip_arcs;
   1.131        bool attributes_done = false;
   1.132 -      std::set<std::string> extra_sections;
   1.133  
   1.134        line_num = 0;      
   1.135        readLine();
   1.136 @@ -1222,16 +1133,6 @@
   1.137  	      attributes_done = true;
   1.138  	    }
   1.139  	  } else {
   1.140 -	    if (extra_sections.find(section) != extra_sections.end()) {
   1.141 -	      std::ostringstream msg;
   1.142 -	      msg << "Multiple occurence of section " << section;
   1.143 -	      throw DataFormatError(msg.str().c_str());
   1.144 -	    }
   1.145 -	    Sections::iterator it = _sections.find(section);
   1.146 -	    if (it != _sections.end()) {
   1.147 -	      extra_sections.insert(section);
   1.148 -	      it->second->process(*_is, line_num);
   1.149 -	    }
   1.150  	    readLine();
   1.151  	    skipSection();
   1.152  	  }
   1.153 @@ -1295,7 +1196,6 @@
   1.154      
   1.155    private:
   1.156  
   1.157 -
   1.158      std::istream* _is;
   1.159      bool local_is;
   1.160  
   1.161 @@ -1322,9 +1222,6 @@
   1.162        Attributes;
   1.163      Attributes _attributes;
   1.164  
   1.165 -    typedef std::map<std::string, _reader_bits::Section*> Sections;
   1.166 -    Sections _sections;
   1.167 -
   1.168      bool _use_nodes;
   1.169      bool _use_edges;
   1.170  
   1.171 @@ -1386,7 +1283,6 @@
   1.172        _edges_caption = other._edges_caption;
   1.173        _attributes_caption = other._attributes_caption;
   1.174  
   1.175 -      _sections.swap(other._sections);
   1.176      }
   1.177  
   1.178      /// \brief Destructor
   1.179 @@ -1406,11 +1302,6 @@
   1.180  	delete it->second;
   1.181        }
   1.182  
   1.183 -      for (typename Sections::iterator it = _sections.begin(); 
   1.184 -	   it != _sections.end(); ++it) {
   1.185 -	delete it->second;
   1.186 -      }
   1.187 -
   1.188        if (local_is) {
   1.189  	delete _is;
   1.190        }
   1.191 @@ -1603,83 +1494,6 @@
   1.192  
   1.193      /// @}
   1.194  
   1.195 -    /// \name Section readers
   1.196 -    /// @{
   1.197 -
   1.198 -    /// \brief Add a section processor with line oriented reading
   1.199 -    ///
   1.200 -    /// In the \e LGF file extra sections can be placed, which contain
   1.201 -    /// any data in arbitrary format. These sections can be read with
   1.202 -    /// this function line by line. The first parameter is the type
   1.203 -    /// descriptor of the section, the second is a functor, which
   1.204 -    /// takes just one \c std::string parameter. At the reading
   1.205 -    /// process, each line of the section will be given to the functor
   1.206 -    /// object. However, the empty lines and the comment lines are
   1.207 -    /// filtered out, and the leading whitespaces are stipped from
   1.208 -    /// each processed string.
   1.209 -    ///
   1.210 -    /// For example let's see a section, which contain several
   1.211 -    /// integers, which should be inserted into a vector.
   1.212 -    ///\code
   1.213 -    ///  @numbers
   1.214 -    ///  12 45 23
   1.215 -    ///  4
   1.216 -    ///  23 6
   1.217 -    ///\endcode
   1.218 -    ///
   1.219 -    /// The functor is implemented as an struct:
   1.220 -    ///\code
   1.221 -    ///  struct NumberSection {
   1.222 -    ///    std::vector<int>& _data;
   1.223 -    ///    NumberSection(std::vector<int>& data) : _data(data) {}
   1.224 -    ///    void operator()(const std::string& line) {
   1.225 -    ///      std::istringstream ls(line);
   1.226 -    ///      int value;
   1.227 -    ///      while (ls >> value) _data.push_back(value);
   1.228 -    ///    }
   1.229 -    ///  };
   1.230 -    ///
   1.231 -    ///  // ...
   1.232 -    ///
   1.233 -    ///  reader.sectionLines("numbers", NumberSection(vec));  
   1.234 -    ///\endcode
   1.235 -    template <typename Functor>
   1.236 -    GraphReader& sectionLines(const std::string& type, Functor functor) {
   1.237 -      LEMON_ASSERT(!type.empty(), "Type is not empty.");
   1.238 -      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
   1.239 -		   "Multiple reading of section.");
   1.240 -      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
   1.241 -		   type != "attributes", "Multiple reading of section.");
   1.242 -      _sections.insert(std::make_pair(type, 
   1.243 -        new _reader_bits::LineSection<Functor>(functor)));
   1.244 -      return *this;
   1.245 -    }
   1.246 -
   1.247 -
   1.248 -    /// \brief Add a section processor with stream oriented reading
   1.249 -    ///
   1.250 -    /// In the \e LGF file extra sections can be placed, which contain
   1.251 -    /// any data in arbitrary format. These sections can be read
   1.252 -    /// directly with this function. The first parameter is the type
   1.253 -    /// of the section, the second is a functor, which takes an \c
   1.254 -    /// std::istream& and an int& parameter, the latter regard to the
   1.255 -    /// line number of stream. The functor can read the input while
   1.256 -    /// the section go on, and the line number should be modified
   1.257 -    /// accordingly.
   1.258 -    template <typename Functor>
   1.259 -    GraphReader& sectionStream(const std::string& type, Functor functor) {
   1.260 -      LEMON_ASSERT(!type.empty(), "Type is not empty.");
   1.261 -      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
   1.262 -		   "Multiple reading of section.");
   1.263 -      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
   1.264 -		   type != "attributes", "Multiple reading of section.");
   1.265 -      _sections.insert(std::make_pair(type, 
   1.266 -	 new _reader_bits::StreamSection<Functor>(functor)));
   1.267 -      return *this;
   1.268 -    }    
   1.269 -    
   1.270 -    /// @}
   1.271 -
   1.272      /// \name Using previously constructed node or edge set
   1.273      /// @{
   1.274  
   1.275 @@ -2081,7 +1895,6 @@
   1.276        bool nodes_done = _skip_nodes;
   1.277        bool edges_done = _skip_edges;
   1.278        bool attributes_done = false;
   1.279 -      std::set<std::string> extra_sections;
   1.280  
   1.281        line_num = 0;      
   1.282        readLine();
   1.283 @@ -2115,16 +1928,6 @@
   1.284  	      attributes_done = true;
   1.285  	    }
   1.286  	  } else {
   1.287 -	    if (extra_sections.find(section) != extra_sections.end()) {
   1.288 -	      std::ostringstream msg;
   1.289 -	      msg << "Multiple occurence of section " << section;
   1.290 -	      throw DataFormatError(msg.str().c_str());
   1.291 -	    }
   1.292 -	    Sections::iterator it = _sections.find(section);
   1.293 -	    if (it != _sections.end()) {
   1.294 -	      extra_sections.insert(section);
   1.295 -	      it->second->process(*_is, line_num);
   1.296 -	    }
   1.297  	    readLine();
   1.298  	    skipSection();
   1.299  	  }
   1.300 @@ -2174,6 +1977,253 @@
   1.301      return tmp;
   1.302    }
   1.303  
   1.304 +  /// \brief Section reader class
   1.305 +  ///
   1.306 +  /// In the \e LGF file extra sections can be placed, which contain
   1.307 +  /// any data in arbitrary format. Such sections can be read with
   1.308 +  /// this class. A reading rule can be added with two different
   1.309 +  /// functions, with the \c sectionLines() function a functor can
   1.310 +  /// process the section line-by-line. While with the \c
   1.311 +  /// sectionStream() member the section can be read from an input
   1.312 +  /// stream.
   1.313 +  class SectionReader {
   1.314 +  private:
   1.315 +    
   1.316 +    std::istream* _is;
   1.317 +    bool local_is;
   1.318 +
   1.319 +    typedef std::map<std::string, _reader_bits::Section*> Sections;
   1.320 +    Sections _sections;
   1.321 +
   1.322 +    int line_num;
   1.323 +    std::istringstream line;
   1.324 +
   1.325 +  public:
   1.326 +
   1.327 +    /// \brief Constructor
   1.328 +    ///
   1.329 +    /// Construct a section reader, which reads from the given input
   1.330 +    /// stream.
   1.331 +    SectionReader(std::istream& is) 
   1.332 +      : _is(&is), local_is(false) {}
   1.333 +
   1.334 +    /// \brief Constructor
   1.335 +    ///
   1.336 +    /// Construct a section reader, which reads from the given file.
   1.337 +    SectionReader(const std::string& fn) 
   1.338 +      : _is(new std::ifstream(fn.c_str())), local_is(true) {}
   1.339 +    
   1.340 +    /// \brief Constructor
   1.341 +    ///
   1.342 +    /// Construct a section reader, which reads from the given file.
   1.343 +    SectionReader(const char* fn) 
   1.344 +      : _is(new std::ifstream(fn)), local_is(true) {}
   1.345 +
   1.346 +    /// \brief Copy constructor
   1.347 +    ///
   1.348 +    /// The copy constructor transfers all data from the other reader,
   1.349 +    /// therefore the copied reader will not be usable more. 
   1.350 +    SectionReader(SectionReader& other) 
   1.351 +      : _is(other._is), local_is(other.local_is) {
   1.352 +
   1.353 +      other._is = 0;
   1.354 +      other.local_is = false;
   1.355 +      
   1.356 +      _sections.swap(other._sections);
   1.357 +    }
   1.358 +
   1.359 +    /// \brief Destructor
   1.360 +    ~SectionReader() {
   1.361 +      for (Sections::iterator it = _sections.begin(); 
   1.362 +	   it != _sections.end(); ++it) {
   1.363 +	delete it->second;
   1.364 +      }
   1.365 +
   1.366 +      if (local_is) {
   1.367 +	delete _is;
   1.368 +      }
   1.369 +
   1.370 +    }
   1.371 +
   1.372 +  private:
   1.373 +    
   1.374 +    SectionReader& operator=(const SectionReader&);
   1.375 +
   1.376 +  public:
   1.377 +
   1.378 +    /// \name Section readers
   1.379 +    /// @{
   1.380 +
   1.381 +    /// \brief Add a section processor with line oriented reading
   1.382 +    ///
   1.383 +    /// The first parameter is the type descriptor of the section, the
   1.384 +    /// second is a functor, which takes just one \c std::string
   1.385 +    /// parameter. At the reading process, each line of the section
   1.386 +    /// will be given to the functor object. However, the empty lines
   1.387 +    /// and the comment lines are filtered out, and the leading
   1.388 +    /// whitespaces are trimmed from each processed string.
   1.389 +    ///
   1.390 +    /// For example let's see a section, which contain several
   1.391 +    /// integers, which should be inserted into a vector.
   1.392 +    ///\code
   1.393 +    ///  @numbers
   1.394 +    ///  12 45 23
   1.395 +    ///  4
   1.396 +    ///  23 6
   1.397 +    ///\endcode
   1.398 +    ///
   1.399 +    /// The functor is implemented as an struct:
   1.400 +    ///\code
   1.401 +    ///  struct NumberSection {
   1.402 +    ///    std::vector<int>& _data;
   1.403 +    ///    NumberSection(std::vector<int>& data) : _data(data) {}
   1.404 +    ///    void operator()(const std::string& line) {
   1.405 +    ///      std::istringstream ls(line);
   1.406 +    ///      int value;
   1.407 +    ///      while (ls >> value) _data.push_back(value);
   1.408 +    ///    }
   1.409 +    ///  };
   1.410 +    ///
   1.411 +    ///  // ...
   1.412 +    ///
   1.413 +    ///  reader.sectionLines("numbers", NumberSection(vec));  
   1.414 +    ///\endcode
   1.415 +    template <typename Functor>
   1.416 +    SectionReader& sectionLines(const std::string& type, Functor functor) {
   1.417 +      LEMON_ASSERT(!type.empty(), "Type is not empty.");
   1.418 +      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
   1.419 +		   "Multiple reading of section.");
   1.420 +      _sections.insert(std::make_pair(type, 
   1.421 +        new _reader_bits::LineSection<Functor>(functor)));
   1.422 +      return *this;
   1.423 +    }
   1.424 +
   1.425 +
   1.426 +    /// \brief Add a section processor with stream oriented reading
   1.427 +    ///
   1.428 +    /// The first parameter is the type of the section, the second is
   1.429 +    /// a functor, which takes an \c std::istream& and an int&
   1.430 +    /// parameter, the latter regard to the line number of stream. The
   1.431 +    /// functor can read the input while the section go on, and the
   1.432 +    /// line number should be modified accordingly.
   1.433 +    template <typename Functor>
   1.434 +    SectionReader& sectionStream(const std::string& type, Functor functor) {
   1.435 +      LEMON_ASSERT(!type.empty(), "Type is not empty.");
   1.436 +      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
   1.437 +		   "Multiple reading of section.");
   1.438 +      _sections.insert(std::make_pair(type, 
   1.439 +	 new _reader_bits::StreamSection<Functor>(functor)));
   1.440 +      return *this;
   1.441 +    }    
   1.442 +    
   1.443 +    /// @}
   1.444 +
   1.445 +  private:
   1.446 +
   1.447 +    bool readLine() {
   1.448 +      std::string str;
   1.449 +      while(++line_num, std::getline(*_is, str)) {
   1.450 +	line.clear(); line.str(str);
   1.451 +	char c;
   1.452 +	if (line >> std::ws >> c && c != '#') {
   1.453 +	  line.putback(c);
   1.454 +	  return true;
   1.455 +	}
   1.456 +      }
   1.457 +      return false;
   1.458 +    }
   1.459 +
   1.460 +    bool readSuccess() {
   1.461 +      return static_cast<bool>(*_is);
   1.462 +    }
   1.463 +    
   1.464 +    void skipSection() {
   1.465 +      char c;
   1.466 +      while (readSuccess() && line >> c && c != '@') {
   1.467 +	readLine();
   1.468 +      }
   1.469 +      line.putback(c);
   1.470 +    }
   1.471 +
   1.472 +  public:
   1.473 +
   1.474 +
   1.475 +    /// \name Execution of the reader    
   1.476 +    /// @{
   1.477 +
   1.478 +    /// \brief Start the batch processing
   1.479 +    ///
   1.480 +    /// This function starts the batch processing
   1.481 +    void run() {
   1.482 +      
   1.483 +      LEMON_ASSERT(_is != 0, "This reader assigned to an other reader");
   1.484 +      
   1.485 +      std::set<std::string> extra_sections;
   1.486 +
   1.487 +      line_num = 0;      
   1.488 +      readLine();
   1.489 +      skipSection();
   1.490 +
   1.491 +      while (readSuccess()) {
   1.492 +	try {
   1.493 +	  char c;
   1.494 +	  std::string section, caption;
   1.495 +	  line >> c;
   1.496 +	  _reader_bits::readToken(line, section);
   1.497 +	  _reader_bits::readToken(line, caption);
   1.498 +
   1.499 +	  if (line >> c) 
   1.500 +	    throw DataFormatError("Extra character on the end of line");
   1.501 +
   1.502 +	  if (extra_sections.find(section) != extra_sections.end()) {
   1.503 +	    std::ostringstream msg;
   1.504 +	    msg << "Multiple occurence of section " << section;
   1.505 +	    throw DataFormatError(msg.str().c_str());
   1.506 +	  }
   1.507 +	  Sections::iterator it = _sections.find(section);
   1.508 +	  if (it != _sections.end()) {
   1.509 +	    extra_sections.insert(section);
   1.510 +	    it->second->process(*_is, line_num);
   1.511 +	  }
   1.512 +	  readLine();
   1.513 +	  skipSection();
   1.514 +	} catch (DataFormatError& error) {
   1.515 +	  error.line(line_num);
   1.516 +	  throw;
   1.517 +	}	
   1.518 +      }
   1.519 +      for (Sections::iterator it = _sections.begin();
   1.520 +	   it != _sections.end(); ++it) {
   1.521 +	if (extra_sections.find(it->first) == extra_sections.end()) {
   1.522 +	  std::ostringstream os;
   1.523 +	  os << "Cannot find section: " << it->first;
   1.524 +	  throw DataFormatError(os.str().c_str());
   1.525 +	}
   1.526 +      }
   1.527 +    }
   1.528 +
   1.529 +    /// @}
   1.530 +        
   1.531 +  };
   1.532 +
   1.533 +  /// \relates SectionReader
   1.534 +  inline SectionReader sectionReader(std::istream& is) {
   1.535 +    SectionReader tmp(is);
   1.536 +    return tmp;
   1.537 +  }
   1.538 +
   1.539 +  /// \relates SectionReader
   1.540 +  inline SectionReader sectionReader(const std::string& fn) {
   1.541 +    SectionReader tmp(fn);
   1.542 +    return tmp;
   1.543 +  }
   1.544 +
   1.545 +  /// \relates SectionReader
   1.546 +  inline SectionReader sectionReader(const char* fn) {
   1.547 +    SectionReader tmp(fn);
   1.548 +    return tmp;
   1.549 +  }
   1.550 +
   1.551    /// \ingroup lemon_io
   1.552    ///
   1.553    /// \brief Reader for the contents of the \ref lgf-format "LGF" file