gravatar
deba@inf.elte.hu
deba@inf.elte.hu
Section readers moved to distinct class
0 1 0
default
1 file changed with 248 insertions and 198 deletions:
↑ Collapse diff ↑
Ignore white space 12 line context
... ...
@@ -416,15 +416,13 @@
416 416
  ///       run();
417 417
  ///\endcode
418 418
  ///
419 419
  /// By default the reader uses the first section in the file of the
420 420
  /// proper type. If a section has an optional name, then it can be
421 421
  /// selected for reading by giving an optional name parameter to the
422
  /// \c nodes(), \c arcs() or \c attributes() functions. The readers
423
  /// also can load extra sections with the \c sectionLines() and
424
  /// sectionStream() functions.
422
  /// \c nodes(), \c arcs() or \c attributes() functions.
425 423
  ///
426 424
  /// The \c useNodes() and \c useArcs() functions are used to tell the reader
427 425
  /// that the nodes or arcs should not be constructed (added to the
428 426
  /// graph) during the reading, but instead the label map of the items
429 427
  /// are given as a parameter of these functions. An
430 428
  /// application of these function is multipass reading, which is
... ...
@@ -470,15 +468,12 @@
470 468
    ArcMaps _arc_maps;
471 469

	
472 470
    typedef std::multimap<std::string, _reader_bits::ValueStorageBase*> 
473 471
      Attributes;
474 472
    Attributes _attributes;
475 473

	
476
    typedef std::map<std::string, _reader_bits::Section*> Sections;
477
    Sections _sections;
478

	
479 474
    bool _use_nodes;
480 475
    bool _use_arcs;
481 476

	
482 477
    bool _skip_nodes;
483 478
    bool _skip_arcs;
484 479

	
... ...
@@ -534,13 +529,12 @@
534 529
      _attributes.swap(other._attributes);
535 530

	
536 531
      _nodes_caption = other._nodes_caption;
537 532
      _arcs_caption = other._arcs_caption;
538 533
      _attributes_caption = other._attributes_caption;
539 534

	
540
      _sections.swap(other._sections);
541 535
    }
542 536

	
543 537
    /// \brief Destructor
544 538
    ~DigraphReader() {
545 539
      for (typename NodeMaps::iterator it = _node_maps.begin(); 
546 540
	   it != _node_maps.end(); ++it) {
... ...
@@ -554,17 +548,12 @@
554 548

	
555 549
      for (typename Attributes::iterator it = _attributes.begin(); 
556 550
	   it != _attributes.end(); ++it) {
557 551
	delete it->second;
558 552
      }
559 553

	
560
      for (typename Sections::iterator it = _sections.begin(); 
561
	   it != _sections.end(); ++it) {
562
	delete it->second;
563
      }
564

	
565 554
      if (local_is) {
566 555
	delete _is;
567 556
      }
568 557

	
569 558
    }
570 559

	
... ...
@@ -705,89 +694,12 @@
705 694
      _attributes_caption = caption;
706 695
      return *this;
707 696
    }
708 697

	
709 698
    /// @}
710 699

	
711
    /// \name Section readers
712
    /// @{
713

	
714
    /// \brief Add a section processor with line oriented reading
715
    ///
716
    /// In the \e LGF file extra sections can be placed, which contain
717
    /// any data in arbitrary format. These sections can be read with
718
    /// this function line by line. The first parameter is the type
719
    /// descriptor of the section, the second is a functor, which
720
    /// takes just one \c std::string parameter. At the reading
721
    /// process, each line of the section will be given to the functor
722
    /// object. However, the empty lines and the comment lines are
723
    /// filtered out, and the leading whitespaces are stipped from
724
    /// each processed string.
725
    ///
726
    /// For example let's see a section, which contain several
727
    /// integers, which should be inserted into a vector.
728
    ///\code
729
    ///  @numbers
730
    ///  12 45 23
731
    ///  4
732
    ///  23 6
733
    ///\endcode
734
    ///
735
    /// The functor is implemented as an struct:
736
    ///\code
737
    ///  struct NumberSection {
738
    ///    std::vector<int>& _data;
739
    ///    NumberSection(std::vector<int>& data) : _data(data) {}
740
    ///    void operator()(const std::string& line) {
741
    ///      std::istringstream ls(line);
742
    ///      int value;
743
    ///      while (ls >> value) _data.push_back(value);
744
    ///    }
745
    ///  };
746
    ///
747
    ///  // ...
748
    ///
749
    ///  reader.sectionLines("numbers", NumberSection(vec));  
750
    ///\endcode
751
    template <typename Functor>
752
    DigraphReader& sectionLines(const std::string& type, Functor functor) {
753
      LEMON_ASSERT(!type.empty(), "Type is not empty.");
754
      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
755
		   "Multiple reading of section.");
756
      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
757
		   type != "attributes", "Multiple reading of section.");
758
      _sections.insert(std::make_pair(type, 
759
        new _reader_bits::LineSection<Functor>(functor)));
760
      return *this;
761
    }
762

	
763

	
764
    /// \brief Add a section processor with stream oriented reading
765
    ///
766
    /// In the \e LGF file extra sections can be placed, which contain
767
    /// any data in arbitrary format. These sections can be read
768
    /// directly with this function. The first parameter is the type
769
    /// of the section, the second is a functor, which takes an \c
770
    /// std::istream& and an int& parameter, the latter regard to the
771
    /// line number of stream. The functor can read the input while
772
    /// the section go on, and the line number should be modified
773
    /// accordingly.
774
    template <typename Functor>
775
    DigraphReader& sectionStream(const std::string& type, Functor functor) {
776
      LEMON_ASSERT(!type.empty(), "Type is not empty.");
777
      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
778
		   "Multiple reading of section.");
779
      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
780
		   type != "attributes", "Multiple reading of section.");
781
      _sections.insert(std::make_pair(type, 
782
	 new _reader_bits::StreamSection<Functor>(functor)));
783
      return *this;
784
    }    
785
    
786
    /// @}
787

	
788 700
    /// \name Using previously constructed node or arc set
789 701
    /// @{
790 702

	
791 703
    /// \brief Use previously constructed node set
792 704
    ///
793 705
    /// Use previously constructed node set, and specify the node
... ...
@@ -1185,13 +1097,12 @@
1185 1097
	throw DataFormatError("Cannot find file");
1186 1098
      }
1187 1099
      
1188 1100
      bool nodes_done = _skip_nodes;
1189 1101
      bool arcs_done = _skip_arcs;
1190 1102
      bool attributes_done = false;
1191
      std::set<std::string> extra_sections;
1192 1103

	
1193 1104
      line_num = 0;      
1194 1105
      readLine();
1195 1106
      skipSection();
1196 1107

	
1197 1108
      while (readSuccess()) {
... ...
@@ -1219,22 +1130,12 @@
1219 1130
	  } else if (section == "attributes" && !attributes_done) {
1220 1131
	    if (_attributes_caption.empty() || _attributes_caption == caption) {
1221 1132
	      readAttributes();
1222 1133
	      attributes_done = true;
1223 1134
	    }
1224 1135
	  } else {
1225
	    if (extra_sections.find(section) != extra_sections.end()) {
1226
	      std::ostringstream msg;
1227
	      msg << "Multiple occurence of section " << section;
1228
	      throw DataFormatError(msg.str().c_str());
1229
	    }
1230
	    Sections::iterator it = _sections.find(section);
1231
	    if (it != _sections.end()) {
1232
	      extra_sections.insert(section);
1233
	      it->second->process(*_is, line_num);
1234
	    }
1235 1136
	    readLine();
1236 1137
	    skipSection();
1237 1138
	  }
1238 1139
	} catch (DataFormatError& error) {
1239 1140
	  error.line(line_num);
1240 1141
	  throw;
... ...
@@ -1292,13 +1193,12 @@
1292 1193

	
1293 1194
    typedef _Graph Graph;
1294 1195
    TEMPLATE_GRAPH_TYPEDEFS(Graph);
1295 1196
    
1296 1197
  private:
1297 1198

	
1298

	
1299 1199
    std::istream* _is;
1300 1200
    bool local_is;
1301 1201

	
1302 1202
    Graph& _graph;
1303 1203

	
1304 1204
    std::string _nodes_caption;
... ...
@@ -1319,15 +1219,12 @@
1319 1219
    EdgeMaps _edge_maps;
1320 1220

	
1321 1221
    typedef std::multimap<std::string, _reader_bits::ValueStorageBase*> 
1322 1222
      Attributes;
1323 1223
    Attributes _attributes;
1324 1224

	
1325
    typedef std::map<std::string, _reader_bits::Section*> Sections;
1326
    Sections _sections;
1327

	
1328 1225
    bool _use_nodes;
1329 1226
    bool _use_edges;
1330 1227

	
1331 1228
    bool _skip_nodes;
1332 1229
    bool _skip_edges;
1333 1230

	
... ...
@@ -1383,13 +1280,12 @@
1383 1280
      _attributes.swap(other._attributes);
1384 1281

	
1385 1282
      _nodes_caption = other._nodes_caption;
1386 1283
      _edges_caption = other._edges_caption;
1387 1284
      _attributes_caption = other._attributes_caption;
1388 1285

	
1389
      _sections.swap(other._sections);
1390 1286
    }
1391 1287

	
1392 1288
    /// \brief Destructor
1393 1289
    ~GraphReader() {
1394 1290
      for (typename NodeMaps::iterator it = _node_maps.begin(); 
1395 1291
	   it != _node_maps.end(); ++it) {
... ...
@@ -1403,17 +1299,12 @@
1403 1299

	
1404 1300
      for (typename Attributes::iterator it = _attributes.begin(); 
1405 1301
	   it != _attributes.end(); ++it) {
1406 1302
	delete it->second;
1407 1303
      }
1408 1304

	
1409
      for (typename Sections::iterator it = _sections.begin(); 
1410
	   it != _sections.end(); ++it) {
1411
	delete it->second;
1412
      }
1413

	
1414 1305
      if (local_is) {
1415 1306
	delete _is;
1416 1307
      }
1417 1308

	
1418 1309
    }
1419 1310

	
... ...
@@ -1600,89 +1491,12 @@
1600 1491
      _attributes_caption = caption;
1601 1492
      return *this;
1602 1493
    }
1603 1494

	
1604 1495
    /// @}
1605 1496

	
1606
    /// \name Section readers
1607
    /// @{
1608

	
1609
    /// \brief Add a section processor with line oriented reading
1610
    ///
1611
    /// In the \e LGF file extra sections can be placed, which contain
1612
    /// any data in arbitrary format. These sections can be read with
1613
    /// this function line by line. The first parameter is the type
1614
    /// descriptor of the section, the second is a functor, which
1615
    /// takes just one \c std::string parameter. At the reading
1616
    /// process, each line of the section will be given to the functor
1617
    /// object. However, the empty lines and the comment lines are
1618
    /// filtered out, and the leading whitespaces are stipped from
1619
    /// each processed string.
1620
    ///
1621
    /// For example let's see a section, which contain several
1622
    /// integers, which should be inserted into a vector.
1623
    ///\code
1624
    ///  @numbers
1625
    ///  12 45 23
1626
    ///  4
1627
    ///  23 6
1628
    ///\endcode
1629
    ///
1630
    /// The functor is implemented as an struct:
1631
    ///\code
1632
    ///  struct NumberSection {
1633
    ///    std::vector<int>& _data;
1634
    ///    NumberSection(std::vector<int>& data) : _data(data) {}
1635
    ///    void operator()(const std::string& line) {
1636
    ///      std::istringstream ls(line);
1637
    ///      int value;
1638
    ///      while (ls >> value) _data.push_back(value);
1639
    ///    }
1640
    ///  };
1641
    ///
1642
    ///  // ...
1643
    ///
1644
    ///  reader.sectionLines("numbers", NumberSection(vec));  
1645
    ///\endcode
1646
    template <typename Functor>
1647
    GraphReader& sectionLines(const std::string& type, Functor functor) {
1648
      LEMON_ASSERT(!type.empty(), "Type is not empty.");
1649
      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
1650
		   "Multiple reading of section.");
1651
      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
1652
		   type != "attributes", "Multiple reading of section.");
1653
      _sections.insert(std::make_pair(type, 
1654
        new _reader_bits::LineSection<Functor>(functor)));
1655
      return *this;
1656
    }
1657

	
1658

	
1659
    /// \brief Add a section processor with stream oriented reading
1660
    ///
1661
    /// In the \e LGF file extra sections can be placed, which contain
1662
    /// any data in arbitrary format. These sections can be read
1663
    /// directly with this function. The first parameter is the type
1664
    /// of the section, the second is a functor, which takes an \c
1665
    /// std::istream& and an int& parameter, the latter regard to the
1666
    /// line number of stream. The functor can read the input while
1667
    /// the section go on, and the line number should be modified
1668
    /// accordingly.
1669
    template <typename Functor>
1670
    GraphReader& sectionStream(const std::string& type, Functor functor) {
1671
      LEMON_ASSERT(!type.empty(), "Type is not empty.");
1672
      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
1673
		   "Multiple reading of section.");
1674
      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
1675
		   type != "attributes", "Multiple reading of section.");
1676
      _sections.insert(std::make_pair(type, 
1677
	 new _reader_bits::StreamSection<Functor>(functor)));
1678
      return *this;
1679
    }    
1680
    
1681
    /// @}
1682

	
1683 1497
    /// \name Using previously constructed node or edge set
1684 1498
    /// @{
1685 1499

	
1686 1500
    /// \brief Use previously constructed node set
1687 1501
    ///
1688 1502
    /// Use previously constructed node set, and specify the node
... ...
@@ -2078,13 +1892,12 @@
2078 1892
      
2079 1893
      LEMON_ASSERT(_is != 0, "This reader assigned to an other reader");
2080 1894
      
2081 1895
      bool nodes_done = _skip_nodes;
2082 1896
      bool edges_done = _skip_edges;
2083 1897
      bool attributes_done = false;
2084
      std::set<std::string> extra_sections;
2085 1898

	
2086 1899
      line_num = 0;      
2087 1900
      readLine();
2088 1901
      skipSection();
2089 1902

	
2090 1903
      while (readSuccess()) {
... ...
@@ -2112,22 +1925,12 @@
2112 1925
	  } else if (section == "attributes" && !attributes_done) {
2113 1926
	    if (_attributes_caption.empty() || _attributes_caption == caption) {
2114 1927
	      readAttributes();
2115 1928
	      attributes_done = true;
2116 1929
	    }
2117 1930
	  } else {
2118
	    if (extra_sections.find(section) != extra_sections.end()) {
2119
	      std::ostringstream msg;
2120
	      msg << "Multiple occurence of section " << section;
2121
	      throw DataFormatError(msg.str().c_str());
2122
	    }
2123
	    Sections::iterator it = _sections.find(section);
2124
	    if (it != _sections.end()) {
2125
	      extra_sections.insert(section);
2126
	      it->second->process(*_is, line_num);
2127
	    }
2128 1931
	    readLine();
2129 1932
	    skipSection();
2130 1933
	  }
2131 1934
	} catch (DataFormatError& error) {
2132 1935
	  error.line(line_num);
2133 1936
	  throw;
... ...
@@ -2171,12 +1974,259 @@
2171 1974
  template <typename Graph>
2172 1975
  GraphReader<Graph> graphReader(const char* fn, Graph& graph) {
2173 1976
    GraphReader<Graph> tmp(fn, graph);
2174 1977
    return tmp;
2175 1978
  }
2176 1979

	
1980
  /// \brief Section reader class
1981
  ///
1982
  /// In the \e LGF file extra sections can be placed, which contain
1983
  /// any data in arbitrary format. Such sections can be read with
1984
  /// this class. A reading rule can be added with two different
1985
  /// functions, with the \c sectionLines() function a functor can
1986
  /// process the section line-by-line. While with the \c
1987
  /// sectionStream() member the section can be read from an input
1988
  /// stream.
1989
  class SectionReader {
1990
  private:
1991
    
1992
    std::istream* _is;
1993
    bool local_is;
1994

	
1995
    typedef std::map<std::string, _reader_bits::Section*> Sections;
1996
    Sections _sections;
1997

	
1998
    int line_num;
1999
    std::istringstream line;
2000

	
2001
  public:
2002

	
2003
    /// \brief Constructor
2004
    ///
2005
    /// Construct a section reader, which reads from the given input
2006
    /// stream.
2007
    SectionReader(std::istream& is) 
2008
      : _is(&is), local_is(false) {}
2009

	
2010
    /// \brief Constructor
2011
    ///
2012
    /// Construct a section reader, which reads from the given file.
2013
    SectionReader(const std::string& fn) 
2014
      : _is(new std::ifstream(fn.c_str())), local_is(true) {}
2015
    
2016
    /// \brief Constructor
2017
    ///
2018
    /// Construct a section reader, which reads from the given file.
2019
    SectionReader(const char* fn) 
2020
      : _is(new std::ifstream(fn)), local_is(true) {}
2021

	
2022
    /// \brief Copy constructor
2023
    ///
2024
    /// The copy constructor transfers all data from the other reader,
2025
    /// therefore the copied reader will not be usable more. 
2026
    SectionReader(SectionReader& other) 
2027
      : _is(other._is), local_is(other.local_is) {
2028

	
2029
      other._is = 0;
2030
      other.local_is = false;
2031
      
2032
      _sections.swap(other._sections);
2033
    }
2034

	
2035
    /// \brief Destructor
2036
    ~SectionReader() {
2037
      for (Sections::iterator it = _sections.begin(); 
2038
	   it != _sections.end(); ++it) {
2039
	delete it->second;
2040
      }
2041

	
2042
      if (local_is) {
2043
	delete _is;
2044
      }
2045

	
2046
    }
2047

	
2048
  private:
2049
    
2050
    SectionReader& operator=(const SectionReader&);
2051

	
2052
  public:
2053

	
2054
    /// \name Section readers
2055
    /// @{
2056

	
2057
    /// \brief Add a section processor with line oriented reading
2058
    ///
2059
    /// The first parameter is the type descriptor of the section, the
2060
    /// second is a functor, which takes just one \c std::string
2061
    /// parameter. At the reading process, each line of the section
2062
    /// will be given to the functor object. However, the empty lines
2063
    /// and the comment lines are filtered out, and the leading
2064
    /// whitespaces are trimmed from each processed string.
2065
    ///
2066
    /// For example let's see a section, which contain several
2067
    /// integers, which should be inserted into a vector.
2068
    ///\code
2069
    ///  @numbers
2070
    ///  12 45 23
2071
    ///  4
2072
    ///  23 6
2073
    ///\endcode
2074
    ///
2075
    /// The functor is implemented as an struct:
2076
    ///\code
2077
    ///  struct NumberSection {
2078
    ///    std::vector<int>& _data;
2079
    ///    NumberSection(std::vector<int>& data) : _data(data) {}
2080
    ///    void operator()(const std::string& line) {
2081
    ///      std::istringstream ls(line);
2082
    ///      int value;
2083
    ///      while (ls >> value) _data.push_back(value);
2084
    ///    }
2085
    ///  };
2086
    ///
2087
    ///  // ...
2088
    ///
2089
    ///  reader.sectionLines("numbers", NumberSection(vec));  
2090
    ///\endcode
2091
    template <typename Functor>
2092
    SectionReader& sectionLines(const std::string& type, Functor functor) {
2093
      LEMON_ASSERT(!type.empty(), "Type is not empty.");
2094
      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
2095
		   "Multiple reading of section.");
2096
      _sections.insert(std::make_pair(type, 
2097
        new _reader_bits::LineSection<Functor>(functor)));
2098
      return *this;
2099
    }
2100

	
2101

	
2102
    /// \brief Add a section processor with stream oriented reading
2103
    ///
2104
    /// The first parameter is the type of the section, the second is
2105
    /// a functor, which takes an \c std::istream& and an int&
2106
    /// parameter, the latter regard to the line number of stream. The
2107
    /// functor can read the input while the section go on, and the
2108
    /// line number should be modified accordingly.
2109
    template <typename Functor>
2110
    SectionReader& sectionStream(const std::string& type, Functor functor) {
2111
      LEMON_ASSERT(!type.empty(), "Type is not empty.");
2112
      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
2113
		   "Multiple reading of section.");
2114
      _sections.insert(std::make_pair(type, 
2115
	 new _reader_bits::StreamSection<Functor>(functor)));
2116
      return *this;
2117
    }    
2118
    
2119
    /// @}
2120

	
2121
  private:
2122

	
2123
    bool readLine() {
2124
      std::string str;
2125
      while(++line_num, std::getline(*_is, str)) {
2126
	line.clear(); line.str(str);
2127
	char c;
2128
	if (line >> std::ws >> c && c != '#') {
2129
	  line.putback(c);
2130
	  return true;
2131
	}
2132
      }
2133
      return false;
2134
    }
2135

	
2136
    bool readSuccess() {
2137
      return static_cast<bool>(*_is);
2138
    }
2139
    
2140
    void skipSection() {
2141
      char c;
2142
      while (readSuccess() && line >> c && c != '@') {
2143
	readLine();
2144
      }
2145
      line.putback(c);
2146
    }
2147

	
2148
  public:
2149

	
2150

	
2151
    /// \name Execution of the reader    
2152
    /// @{
2153

	
2154
    /// \brief Start the batch processing
2155
    ///
2156
    /// This function starts the batch processing
2157
    void run() {
2158
      
2159
      LEMON_ASSERT(_is != 0, "This reader assigned to an other reader");
2160
      
2161
      std::set<std::string> extra_sections;
2162

	
2163
      line_num = 0;      
2164
      readLine();
2165
      skipSection();
2166

	
2167
      while (readSuccess()) {
2168
	try {
2169
	  char c;
2170
	  std::string section, caption;
2171
	  line >> c;
2172
	  _reader_bits::readToken(line, section);
2173
	  _reader_bits::readToken(line, caption);
2174

	
2175
	  if (line >> c) 
2176
	    throw DataFormatError("Extra character on the end of line");
2177

	
2178
	  if (extra_sections.find(section) != extra_sections.end()) {
2179
	    std::ostringstream msg;
2180
	    msg << "Multiple occurence of section " << section;
2181
	    throw DataFormatError(msg.str().c_str());
2182
	  }
2183
	  Sections::iterator it = _sections.find(section);
2184
	  if (it != _sections.end()) {
2185
	    extra_sections.insert(section);
2186
	    it->second->process(*_is, line_num);
2187
	  }
2188
	  readLine();
2189
	  skipSection();
2190
	} catch (DataFormatError& error) {
2191
	  error.line(line_num);
2192
	  throw;
2193
	}	
2194
      }
2195
      for (Sections::iterator it = _sections.begin();
2196
	   it != _sections.end(); ++it) {
2197
	if (extra_sections.find(it->first) == extra_sections.end()) {
2198
	  std::ostringstream os;
2199
	  os << "Cannot find section: " << it->first;
2200
	  throw DataFormatError(os.str().c_str());
2201
	}
2202
      }
2203
    }
2204

	
2205
    /// @}
2206
        
2207
  };
2208

	
2209
  /// \relates SectionReader
2210
  inline SectionReader sectionReader(std::istream& is) {
2211
    SectionReader tmp(is);
2212
    return tmp;
2213
  }
2214

	
2215
  /// \relates SectionReader
2216
  inline SectionReader sectionReader(const std::string& fn) {
2217
    SectionReader tmp(fn);
2218
    return tmp;
2219
  }
2220

	
2221
  /// \relates SectionReader
2222
  inline SectionReader sectionReader(const char* fn) {
2223
    SectionReader tmp(fn);
2224
    return tmp;
2225
  }
2226

	
2177 2227
  /// \ingroup lemon_io
2178 2228
  ///
2179 2229
  /// \brief Reader for the contents of the \ref lgf-format "LGF" file 
2180 2230
  ///
2181 2231
  /// This class can be used to read the sections, the map names and
2182 2232
  /// the attributes from a file. Usually, the Lemon programs know
0 comments (0 inline)