Section reader for DigraphReader
authorBalazs Dezso <deba@inf.elte.hu>
Tue, 27 May 2008 16:01:20 +0200
changeset 16233247f6fff16
parent 161 2c999941b871
child 163 c82fd9568d75
Section reader for DigraphReader
doc/lgf.dox
lemon/lgf_reader.h
     1.1 --- a/doc/lgf.dox	Mon May 26 13:50:47 2008 +0100
     1.2 +++ b/doc/lgf.dox	Tue May 27 16:01:20 2008 +0200
     1.3 @@ -90,6 +90,9 @@
     1.4   caption "LEMON test digraph"
     1.5  \endcode
     1.6  
     1.7 +The \e LGF can contain extra sections, but there is no restriction on
     1.8 +the format of such sections.
     1.9 +
    1.10  */
    1.11  }
    1.12  
     2.1 --- a/lemon/lgf_reader.h	Mon May 26 13:50:47 2008 +0100
     2.2 +++ b/lemon/lgf_reader.h	Tue May 27 16:01:20 2008 +0200
     2.3 @@ -268,6 +268,72 @@
     2.4        str = os.str();
     2.5        return is;
     2.6      }
     2.7 +
     2.8 +    class Section {
     2.9 +    public:
    2.10 +      virtual ~Section() {}
    2.11 +      virtual void process(std::istream& is, int& line_num) = 0;
    2.12 +    };
    2.13 +
    2.14 +    template <typename Functor>
    2.15 +    class LineSection : public Section {
    2.16 +    private:
    2.17 +
    2.18 +      Functor _functor;
    2.19 +
    2.20 +    public:
    2.21 +      
    2.22 +      LineSection(const Functor& functor) : _functor(functor) {}
    2.23 +      virtual ~LineSection() {}
    2.24 +
    2.25 +      virtual void process(std::istream& is, int& line_num) {
    2.26 +	char c;
    2.27 +	std::string line;
    2.28 +	while (is.get(c) && c != '@') {
    2.29 +	  if (c == '\n') {
    2.30 +	    ++line_num;
    2.31 +	  } else if (c == '#') {
    2.32 +	    getline(is, line);
    2.33 +	    ++line_num;
    2.34 +	  } else if (!isWhiteSpace(c)) {
    2.35 +	    is.putback(c);
    2.36 +	    getline(is, line);
    2.37 +	    _functor(line);
    2.38 +	    ++line_num;
    2.39 +	  }
    2.40 +	}
    2.41 +	if (is) is.putback(c);
    2.42 +	else if (is.eof()) is.clear();
    2.43 +      }
    2.44 +    };
    2.45 +
    2.46 +    template <typename Functor>
    2.47 +    class StreamSection : public Section {
    2.48 +    private:
    2.49 +
    2.50 +      Functor _functor;
    2.51 +
    2.52 +    public:
    2.53 +      
    2.54 +      StreamSection(const Functor& functor) : _functor(functor) {}
    2.55 +      virtual ~StreamSection() {} 
    2.56 +
    2.57 +      virtual void process(std::istream& is, int& line_num) {
    2.58 +	_functor(is, line_num);
    2.59 +	char c;
    2.60 +	std::string line;
    2.61 +	while (is.get(c) && c != '@') {
    2.62 +	  if (c == '\n') {
    2.63 +	    ++line_num;
    2.64 +	  } else if (!isWhiteSpace(c)) {
    2.65 +	    getline(is, line);
    2.66 +	    ++line_num;
    2.67 +	  }
    2.68 +	}
    2.69 +	if (is) is.putback(c);
    2.70 +	else if (is.eof()) is.clear();	
    2.71 +      }
    2.72 +    };
    2.73      
    2.74    }
    2.75  
    2.76 @@ -282,13 +348,14 @@
    2.77    /// reader, and eventually the reading is executed with the \c run()
    2.78    /// member function. A map reading rule can be added to the reader
    2.79    /// with the \c nodeMap() or \c arcMap() members. An optional
    2.80 -  /// converter parameter can also be added as a standard functor converting from
    2.81 -  /// std::string to the value type of the map. If it is set, it will
    2.82 -  /// determine how the tokens in the file should be is converted to the map's
    2.83 -  /// value type. If the functor is not set, then a default conversion
    2.84 -  /// will be used. One map can be read into multiple map objects at the
    2.85 -  /// same time. The \c attribute(), \c node() and \c arc() functions
    2.86 -  /// are used to add attribute reading rules.
    2.87 +  /// converter parameter can also be added as a standard functor
    2.88 +  /// converting from std::string to the value type of the map. If it
    2.89 +  /// is set, it will determine how the tokens in the file should be
    2.90 +  /// is converted to the map's value type. If the functor is not set,
    2.91 +  /// then a default conversion will be used. One map can be read into
    2.92 +  /// multiple map objects at the same time. The \c attribute(), \c
    2.93 +  /// node() and \c arc() functions are used to add attribute reading
    2.94 +  /// rules.
    2.95    ///
    2.96    ///\code
    2.97    ///     DigraphReader<Digraph>(std::cin, digraph).
    2.98 @@ -302,9 +369,10 @@
    2.99    ///
   2.100    /// By default the reader uses the first section in the file of the
   2.101    /// proper type. If a section has an optional name, then it can be
   2.102 -  /// selected for reading by giving an optional name parameter to
   2.103 -  /// the \c nodes(), \c arcs() or \c attributes()
   2.104 -  /// functions.
   2.105 +  /// selected for reading by giving an optional name parameter to the
   2.106 +  /// \c nodes(), \c arcs() or \c attributes() functions. The readers
   2.107 +  /// also can load extra sections with the \c sectionLines() and
   2.108 +  /// sectionStream() functions.
   2.109    ///
   2.110    /// The \c useNodes() and \c useArcs() functions are used to tell the reader
   2.111    /// that the nodes or arcs should not be constructed (added to the
   2.112 @@ -356,6 +424,9 @@
   2.113        Attributes;
   2.114      Attributes _attributes;
   2.115  
   2.116 +    typedef std::map<std::string, _reader_bits::Section*> Sections;
   2.117 +    Sections _sections;
   2.118 +
   2.119      bool _use_nodes;
   2.120      bool _use_arcs;
   2.121  
   2.122 @@ -409,6 +480,8 @@
   2.123        _nodes_caption = other._nodes_caption;
   2.124        _arcs_caption = other._arcs_caption;
   2.125        _attributes_caption = other._attributes_caption;
   2.126 +
   2.127 +      _sections.swap(other._sections);
   2.128      }
   2.129  
   2.130      /// \brief Destructor
   2.131 @@ -428,6 +501,11 @@
   2.132  	delete it->second;
   2.133        }
   2.134  
   2.135 +      for (typename Sections::iterator it = _sections.begin(); 
   2.136 +	   it != _sections.end(); ++it) {
   2.137 +	delete it->second;
   2.138 +      }
   2.139 +
   2.140        if (local_is) {
   2.141  	delete _is;
   2.142        }
   2.143 @@ -574,6 +652,83 @@
   2.144  
   2.145      /// @}
   2.146  
   2.147 +    /// \name Section readers
   2.148 +    /// @{
   2.149 +
   2.150 +    /// \brief Add a section processor with line oriented reading
   2.151 +    ///
   2.152 +    /// In the \e LGF file extra sections can be placed, which contain
   2.153 +    /// any data in arbitrary format. These sections can be read with
   2.154 +    /// this function line by line. The first parameter is the type
   2.155 +    /// descriptor of the section, the second is a functor, which
   2.156 +    /// takes just one \c std::string parameter. At the reading
   2.157 +    /// process, each line of the section will be given to the functor
   2.158 +    /// object. However, the empty lines and the comment lines are
   2.159 +    /// filtered out, and the leading whitespaces are stipped from
   2.160 +    /// each processed string.
   2.161 +    ///
   2.162 +    /// For example let's see a section, which contain several
   2.163 +    /// integers, which should be inserted into a vector.
   2.164 +    ///\code
   2.165 +    ///  @numbers
   2.166 +    ///  12 45 23
   2.167 +    ///  4
   2.168 +    ///  23 6
   2.169 +    ///\endcode
   2.170 +    ///
   2.171 +    /// The functor is implemented as an struct:
   2.172 +    ///\code
   2.173 +    ///  struct NumberSection {
   2.174 +    ///    std::vector<int>& _data;
   2.175 +    ///    NumberSection(std::vector<int>& data) : _data(data) {}
   2.176 +    ///    void operator()(const std::string& line) {
   2.177 +    ///      std::istringstream ls(line);
   2.178 +    ///      int value;
   2.179 +    ///      while (ls >> value) _data.push_back(value);
   2.180 +    ///    }
   2.181 +    ///  };
   2.182 +    ///
   2.183 +    ///  // ...
   2.184 +    ///
   2.185 +    ///  reader.sectionLines("numbers", NumberSection(vec));  
   2.186 +    ///\endcode
   2.187 +    template <typename Functor>
   2.188 +    DigraphReader& sectionLines(const std::string& type, Functor functor) {
   2.189 +      LEMON_ASSERT(!type.empty(), "Type is not empty.");
   2.190 +      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
   2.191 +		   "Multiple reading of section.");
   2.192 +      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
   2.193 +		   type != "attributes", "Multiple reading of section.");
   2.194 +      _sections.insert(std::make_pair(type, 
   2.195 +        new _reader_bits::LineSection<Functor>(functor)));
   2.196 +      return *this;
   2.197 +    }
   2.198 +
   2.199 +
   2.200 +    /// \brief Add a section processor with stream oriented reading
   2.201 +    ///
   2.202 +    /// In the \e LGF file extra sections can be placed, which contain
   2.203 +    /// any data in arbitrary format. These sections can be read
   2.204 +    /// directly with this function. The first parameter is the type
   2.205 +    /// of the section, the second is a functor, which takes an \c
   2.206 +    /// std::istream& and an int& parameter, the latter regard to the
   2.207 +    /// line number of stream. The functor can read the input while
   2.208 +    /// the section go on, and the line number should be modified
   2.209 +    /// accordingly.
   2.210 +    template <typename Functor>
   2.211 +    DigraphReader& sectionStream(const std::string& type, Functor functor) {
   2.212 +      LEMON_ASSERT(!type.empty(), "Type is not empty.");
   2.213 +      LEMON_ASSERT(_sections.find(type) == _sections.end(), 
   2.214 +		   "Multiple reading of section.");
   2.215 +      LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" &&
   2.216 +		   type != "attributes", "Multiple reading of section.");
   2.217 +      _sections.insert(std::make_pair(type, 
   2.218 +	 new _reader_bits::StreamSection<Functor>(functor)));
   2.219 +      return *this;
   2.220 +    }    
   2.221 +    
   2.222 +    /// @}
   2.223 +
   2.224      /// \name Using previously constructed node or arc set
   2.225      /// @{
   2.226  
   2.227 @@ -929,6 +1084,7 @@
   2.228        bool nodes_done = false;
   2.229        bool arcs_done = false;
   2.230        bool attributes_done = false;
   2.231 +      std::set<std::string> extra_sections;
   2.232  
   2.233        line_num = 0;      
   2.234        readLine();
   2.235 @@ -962,8 +1118,20 @@
   2.236  	      attributes_done = true;
   2.237  	    }
   2.238  	  } else {
   2.239 -	    readLine();
   2.240 -	    skipSection();
   2.241 +	    if (extra_sections.find(section) != extra_sections.end()) {
   2.242 +	      std::ostringstream msg;
   2.243 +	      msg << "Multiple occurence of section " << section;
   2.244 +	      throw DataFormatError(msg.str().c_str());
   2.245 +	    }
   2.246 +	    Sections::iterator it = _sections.find(section);
   2.247 +	    if (it != _sections.end()) {
   2.248 +	      extra_sections.insert(section);
   2.249 +	      it->second->process(*_is, line_num);
   2.250 +	      readLine();
   2.251 +	    } else {
   2.252 +	      readLine();
   2.253 +	      skipSection();
   2.254 +	    }
   2.255  	  }
   2.256  	} catch (DataFormatError& error) {
   2.257  	  error.line(line_num);