# HG changeset patch # User Balazs Dezso # Date 2008-07-04 15:21:48 # Node ID a63ed81c57babfd26265bcf154436fb8600c13c3 # Parent 70694e6bdcac801824ae907587fb5f9000dc6b9a Section readers moved to distinct class diff --git a/lemon/lgf_reader.h b/lemon/lgf_reader.h --- a/lemon/lgf_reader.h +++ b/lemon/lgf_reader.h @@ -419,9 +419,7 @@ /// By default the reader uses the first section in the file of the /// proper type. If a section has an optional name, then it can be /// selected for reading by giving an optional name parameter to the - /// \c nodes(), \c arcs() or \c attributes() functions. The readers - /// also can load extra sections with the \c sectionLines() and - /// sectionStream() functions. + /// \c nodes(), \c arcs() or \c attributes() functions. /// /// The \c useNodes() and \c useArcs() functions are used to tell the reader /// that the nodes or arcs should not be constructed (added to the @@ -473,9 +471,6 @@ Attributes; Attributes _attributes; - typedef std::map Sections; - Sections _sections; - bool _use_nodes; bool _use_arcs; @@ -537,7 +532,6 @@ _arcs_caption = other._arcs_caption; _attributes_caption = other._attributes_caption; - _sections.swap(other._sections); } /// \brief Destructor @@ -557,11 +551,6 @@ delete it->second; } - for (typename Sections::iterator it = _sections.begin(); - it != _sections.end(); ++it) { - delete it->second; - } - if (local_is) { delete _is; } @@ -708,83 +697,6 @@ /// @} - /// \name Section readers - /// @{ - - /// \brief Add a section processor with line oriented reading - /// - /// In the \e LGF file extra sections can be placed, which contain - /// any data in arbitrary format. These sections can be read with - /// this function line by line. The first parameter is the type - /// descriptor of the section, the second is a functor, which - /// takes just one \c std::string parameter. At the reading - /// process, each line of the section will be given to the functor - /// object. However, the empty lines and the comment lines are - /// filtered out, and the leading whitespaces are stipped from - /// each processed string. - /// - /// For example let's see a section, which contain several - /// integers, which should be inserted into a vector. - ///\code - /// @numbers - /// 12 45 23 - /// 4 - /// 23 6 - ///\endcode - /// - /// The functor is implemented as an struct: - ///\code - /// struct NumberSection { - /// std::vector& _data; - /// NumberSection(std::vector& data) : _data(data) {} - /// void operator()(const std::string& line) { - /// std::istringstream ls(line); - /// int value; - /// while (ls >> value) _data.push_back(value); - /// } - /// }; - /// - /// // ... - /// - /// reader.sectionLines("numbers", NumberSection(vec)); - ///\endcode - template - DigraphReader& sectionLines(const std::string& type, Functor functor) { - LEMON_ASSERT(!type.empty(), "Type is not empty."); - LEMON_ASSERT(_sections.find(type) == _sections.end(), - "Multiple reading of section."); - LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" && - type != "attributes", "Multiple reading of section."); - _sections.insert(std::make_pair(type, - new _reader_bits::LineSection(functor))); - return *this; - } - - - /// \brief Add a section processor with stream oriented reading - /// - /// In the \e LGF file extra sections can be placed, which contain - /// any data in arbitrary format. These sections can be read - /// directly with this function. The first parameter is the type - /// of the section, the second is a functor, which takes an \c - /// std::istream& and an int& parameter, the latter regard to the - /// line number of stream. The functor can read the input while - /// the section go on, and the line number should be modified - /// accordingly. - template - DigraphReader& sectionStream(const std::string& type, Functor functor) { - LEMON_ASSERT(!type.empty(), "Type is not empty."); - LEMON_ASSERT(_sections.find(type) == _sections.end(), - "Multiple reading of section."); - LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" && - type != "attributes", "Multiple reading of section."); - _sections.insert(std::make_pair(type, - new _reader_bits::StreamSection(functor))); - return *this; - } - - /// @} - /// \name Using previously constructed node or arc set /// @{ @@ -1188,7 +1100,6 @@ bool nodes_done = _skip_nodes; bool arcs_done = _skip_arcs; bool attributes_done = false; - std::set extra_sections; line_num = 0; readLine(); @@ -1222,16 +1133,6 @@ attributes_done = true; } } else { - if (extra_sections.find(section) != extra_sections.end()) { - std::ostringstream msg; - msg << "Multiple occurence of section " << section; - throw DataFormatError(msg.str().c_str()); - } - Sections::iterator it = _sections.find(section); - if (it != _sections.end()) { - extra_sections.insert(section); - it->second->process(*_is, line_num); - } readLine(); skipSection(); } @@ -1295,7 +1196,6 @@ private: - std::istream* _is; bool local_is; @@ -1322,9 +1222,6 @@ Attributes; Attributes _attributes; - typedef std::map Sections; - Sections _sections; - bool _use_nodes; bool _use_edges; @@ -1386,7 +1283,6 @@ _edges_caption = other._edges_caption; _attributes_caption = other._attributes_caption; - _sections.swap(other._sections); } /// \brief Destructor @@ -1406,11 +1302,6 @@ delete it->second; } - for (typename Sections::iterator it = _sections.begin(); - it != _sections.end(); ++it) { - delete it->second; - } - if (local_is) { delete _is; } @@ -1603,83 +1494,6 @@ /// @} - /// \name Section readers - /// @{ - - /// \brief Add a section processor with line oriented reading - /// - /// In the \e LGF file extra sections can be placed, which contain - /// any data in arbitrary format. These sections can be read with - /// this function line by line. The first parameter is the type - /// descriptor of the section, the second is a functor, which - /// takes just one \c std::string parameter. At the reading - /// process, each line of the section will be given to the functor - /// object. However, the empty lines and the comment lines are - /// filtered out, and the leading whitespaces are stipped from - /// each processed string. - /// - /// For example let's see a section, which contain several - /// integers, which should be inserted into a vector. - ///\code - /// @numbers - /// 12 45 23 - /// 4 - /// 23 6 - ///\endcode - /// - /// The functor is implemented as an struct: - ///\code - /// struct NumberSection { - /// std::vector& _data; - /// NumberSection(std::vector& data) : _data(data) {} - /// void operator()(const std::string& line) { - /// std::istringstream ls(line); - /// int value; - /// while (ls >> value) _data.push_back(value); - /// } - /// }; - /// - /// // ... - /// - /// reader.sectionLines("numbers", NumberSection(vec)); - ///\endcode - template - GraphReader& sectionLines(const std::string& type, Functor functor) { - LEMON_ASSERT(!type.empty(), "Type is not empty."); - LEMON_ASSERT(_sections.find(type) == _sections.end(), - "Multiple reading of section."); - LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" && - type != "attributes", "Multiple reading of section."); - _sections.insert(std::make_pair(type, - new _reader_bits::LineSection(functor))); - return *this; - } - - - /// \brief Add a section processor with stream oriented reading - /// - /// In the \e LGF file extra sections can be placed, which contain - /// any data in arbitrary format. These sections can be read - /// directly with this function. The first parameter is the type - /// of the section, the second is a functor, which takes an \c - /// std::istream& and an int& parameter, the latter regard to the - /// line number of stream. The functor can read the input while - /// the section go on, and the line number should be modified - /// accordingly. - template - GraphReader& sectionStream(const std::string& type, Functor functor) { - LEMON_ASSERT(!type.empty(), "Type is not empty."); - LEMON_ASSERT(_sections.find(type) == _sections.end(), - "Multiple reading of section."); - LEMON_ASSERT(type != "nodes" && type != "arcs" && type != "edges" && - type != "attributes", "Multiple reading of section."); - _sections.insert(std::make_pair(type, - new _reader_bits::StreamSection(functor))); - return *this; - } - - /// @} - /// \name Using previously constructed node or edge set /// @{ @@ -2081,7 +1895,6 @@ bool nodes_done = _skip_nodes; bool edges_done = _skip_edges; bool attributes_done = false; - std::set extra_sections; line_num = 0; readLine(); @@ -2115,16 +1928,6 @@ attributes_done = true; } } else { - if (extra_sections.find(section) != extra_sections.end()) { - std::ostringstream msg; - msg << "Multiple occurence of section " << section; - throw DataFormatError(msg.str().c_str()); - } - Sections::iterator it = _sections.find(section); - if (it != _sections.end()) { - extra_sections.insert(section); - it->second->process(*_is, line_num); - } readLine(); skipSection(); } @@ -2174,6 +1977,253 @@ return tmp; } + /// \brief Section reader class + /// + /// In the \e LGF file extra sections can be placed, which contain + /// any data in arbitrary format. Such sections can be read with + /// this class. A reading rule can be added with two different + /// functions, with the \c sectionLines() function a functor can + /// process the section line-by-line. While with the \c + /// sectionStream() member the section can be read from an input + /// stream. + class SectionReader { + private: + + std::istream* _is; + bool local_is; + + typedef std::map Sections; + Sections _sections; + + int line_num; + std::istringstream line; + + public: + + /// \brief Constructor + /// + /// Construct a section reader, which reads from the given input + /// stream. + SectionReader(std::istream& is) + : _is(&is), local_is(false) {} + + /// \brief Constructor + /// + /// Construct a section reader, which reads from the given file. + SectionReader(const std::string& fn) + : _is(new std::ifstream(fn.c_str())), local_is(true) {} + + /// \brief Constructor + /// + /// Construct a section reader, which reads from the given file. + SectionReader(const char* fn) + : _is(new std::ifstream(fn)), local_is(true) {} + + /// \brief Copy constructor + /// + /// The copy constructor transfers all data from the other reader, + /// therefore the copied reader will not be usable more. + SectionReader(SectionReader& other) + : _is(other._is), local_is(other.local_is) { + + other._is = 0; + other.local_is = false; + + _sections.swap(other._sections); + } + + /// \brief Destructor + ~SectionReader() { + for (Sections::iterator it = _sections.begin(); + it != _sections.end(); ++it) { + delete it->second; + } + + if (local_is) { + delete _is; + } + + } + + private: + + SectionReader& operator=(const SectionReader&); + + public: + + /// \name Section readers + /// @{ + + /// \brief Add a section processor with line oriented reading + /// + /// The first parameter is the type descriptor of the section, the + /// second is a functor, which takes just one \c std::string + /// parameter. At the reading process, each line of the section + /// will be given to the functor object. However, the empty lines + /// and the comment lines are filtered out, and the leading + /// whitespaces are trimmed from each processed string. + /// + /// For example let's see a section, which contain several + /// integers, which should be inserted into a vector. + ///\code + /// @numbers + /// 12 45 23 + /// 4 + /// 23 6 + ///\endcode + /// + /// The functor is implemented as an struct: + ///\code + /// struct NumberSection { + /// std::vector& _data; + /// NumberSection(std::vector& data) : _data(data) {} + /// void operator()(const std::string& line) { + /// std::istringstream ls(line); + /// int value; + /// while (ls >> value) _data.push_back(value); + /// } + /// }; + /// + /// // ... + /// + /// reader.sectionLines("numbers", NumberSection(vec)); + ///\endcode + template + SectionReader& sectionLines(const std::string& type, Functor functor) { + LEMON_ASSERT(!type.empty(), "Type is not empty."); + LEMON_ASSERT(_sections.find(type) == _sections.end(), + "Multiple reading of section."); + _sections.insert(std::make_pair(type, + new _reader_bits::LineSection(functor))); + return *this; + } + + + /// \brief Add a section processor with stream oriented reading + /// + /// The first parameter is the type of the section, the second is + /// a functor, which takes an \c std::istream& and an int& + /// parameter, the latter regard to the line number of stream. The + /// functor can read the input while the section go on, and the + /// line number should be modified accordingly. + template + SectionReader& sectionStream(const std::string& type, Functor functor) { + LEMON_ASSERT(!type.empty(), "Type is not empty."); + LEMON_ASSERT(_sections.find(type) == _sections.end(), + "Multiple reading of section."); + _sections.insert(std::make_pair(type, + new _reader_bits::StreamSection(functor))); + return *this; + } + + /// @} + + private: + + bool readLine() { + std::string str; + while(++line_num, std::getline(*_is, str)) { + line.clear(); line.str(str); + char c; + if (line >> std::ws >> c && c != '#') { + line.putback(c); + return true; + } + } + return false; + } + + bool readSuccess() { + return static_cast(*_is); + } + + void skipSection() { + char c; + while (readSuccess() && line >> c && c != '@') { + readLine(); + } + line.putback(c); + } + + public: + + + /// \name Execution of the reader + /// @{ + + /// \brief Start the batch processing + /// + /// This function starts the batch processing + void run() { + + LEMON_ASSERT(_is != 0, "This reader assigned to an other reader"); + + std::set extra_sections; + + line_num = 0; + readLine(); + skipSection(); + + while (readSuccess()) { + try { + char c; + std::string section, caption; + line >> c; + _reader_bits::readToken(line, section); + _reader_bits::readToken(line, caption); + + if (line >> c) + throw DataFormatError("Extra character on the end of line"); + + if (extra_sections.find(section) != extra_sections.end()) { + std::ostringstream msg; + msg << "Multiple occurence of section " << section; + throw DataFormatError(msg.str().c_str()); + } + Sections::iterator it = _sections.find(section); + if (it != _sections.end()) { + extra_sections.insert(section); + it->second->process(*_is, line_num); + } + readLine(); + skipSection(); + } catch (DataFormatError& error) { + error.line(line_num); + throw; + } + } + for (Sections::iterator it = _sections.begin(); + it != _sections.end(); ++it) { + if (extra_sections.find(it->first) == extra_sections.end()) { + std::ostringstream os; + os << "Cannot find section: " << it->first; + throw DataFormatError(os.str().c_str()); + } + } + } + + /// @} + + }; + + /// \relates SectionReader + inline SectionReader sectionReader(std::istream& is) { + SectionReader tmp(is); + return tmp; + } + + /// \relates SectionReader + inline SectionReader sectionReader(const std::string& fn) { + SectionReader tmp(fn); + return tmp; + } + + /// \relates SectionReader + inline SectionReader sectionReader(const char* fn) { + SectionReader tmp(fn); + return tmp; + } + /// \ingroup lemon_io /// /// \brief Reader for the contents of the \ref lgf-format "LGF" file