| 709 | | /// @} |
| 710 | | |
| 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 | | |
| 1604 | | /// @} |
| 1605 | | |
| 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 | | |
| | 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 | /// \name Section readers |
| | 2049 | /// @{ |
| | 2050 | |
| | 2051 | /// \brief Add a section processor with line oriented reading |
| | 2052 | /// |
| | 2053 | /// The first parameter is the type descriptor of the section, the |
| | 2054 | /// second is a functor, which takes just one \c std::string |
| | 2055 | /// parameter. At the reading process, each line of the section |
| | 2056 | /// will be given to the functor object. However, the empty lines |
| | 2057 | /// and the comment lines are filtered out, and the leading |
| | 2058 | /// whitespaces are trimmed from each processed string. |
| | 2059 | /// |
| | 2060 | /// For example let's see a section, which contain several |
| | 2061 | /// integers, which should be inserted into a vector. |
| | 2062 | ///\code |
| | 2063 | /// @numbers |
| | 2064 | /// 12 45 23 |
| | 2065 | /// 4 |
| | 2066 | /// 23 6 |
| | 2067 | ///\endcode |
| | 2068 | /// |
| | 2069 | /// The functor is implemented as an struct: |
| | 2070 | ///\code |
| | 2071 | /// struct NumberSection { |
| | 2072 | /// std::vector<int>& _data; |
| | 2073 | /// NumberSection(std::vector<int>& data) : _data(data) {} |
| | 2074 | /// void operator()(const std::string& line) { |
| | 2075 | /// std::istringstream ls(line); |
| | 2076 | /// int value; |
| | 2077 | /// while (ls >> value) _data.push_back(value); |
| | 2078 | /// } |
| | 2079 | /// }; |
| | 2080 | /// |
| | 2081 | /// // ... |
| | 2082 | /// |
| | 2083 | /// reader.sectionLines("numbers", NumberSection(vec)); |
| | 2084 | ///\endcode |
| | 2085 | template <typename Functor> |
| | 2086 | SectionReader& sectionLines(const std::string& type, Functor functor) { |
| | 2087 | LEMON_ASSERT(!type.empty(), "Type is not empty."); |
| | 2088 | LEMON_ASSERT(_sections.find(type) == _sections.end(), |
| | 2089 | "Multiple reading of section."); |
| | 2090 | _sections.insert(std::make_pair(type, |
| | 2091 | new _reader_bits::LineSection<Functor>(functor))); |
| | 2092 | return *this; |
| | 2093 | } |
| | 2094 | |
| | 2095 | |
| | 2096 | /// \brief Add a section processor with stream oriented reading |
| | 2097 | /// |
| | 2098 | /// The first parameter is the type of the section, the second is |
| | 2099 | /// a functor, which takes an \c std::istream& and an int& |
| | 2100 | /// parameter, the latter regard to the line number of stream. The |
| | 2101 | /// functor can read the input while the section go on, and the |
| | 2102 | /// line number should be modified accordingly. |
| | 2103 | template <typename Functor> |
| | 2104 | SectionReader& sectionStream(const std::string& type, Functor functor) { |
| | 2105 | LEMON_ASSERT(!type.empty(), "Type is not empty."); |
| | 2106 | LEMON_ASSERT(_sections.find(type) == _sections.end(), |
| | 2107 | "Multiple reading of section."); |
| | 2108 | _sections.insert(std::make_pair(type, |
| | 2109 | new _reader_bits::StreamSection<Functor>(functor))); |
| | 2110 | return *this; |
| | 2111 | } |
| | 2112 | |
| | 2113 | /// @} |
| | 2114 | |
| | 2115 | private: |
| | 2116 | |
| | 2117 | bool readLine() { |
| | 2118 | std::string str; |
| | 2119 | while(++line_num, std::getline(*_is, str)) { |
| | 2120 | line.clear(); line.str(str); |
| | 2121 | char c; |
| | 2122 | if (line >> std::ws >> c && c != '#') { |
| | 2123 | line.putback(c); |
| | 2124 | return true; |
| | 2125 | } |
| | 2126 | } |
| | 2127 | return false; |
| | 2128 | } |
| | 2129 | |
| | 2130 | bool readSuccess() { |
| | 2131 | return static_cast<bool>(*_is); |
| | 2132 | } |
| | 2133 | |
| | 2134 | void skipSection() { |
| | 2135 | char c; |
| | 2136 | while (readSuccess() && line >> c && c != '@') { |
| | 2137 | readLine(); |
| | 2138 | } |
| | 2139 | line.putback(c); |
| | 2140 | } |
| | 2141 | |
| | 2142 | public: |
| | 2143 | |
| | 2144 | |
| | 2145 | /// \name Execution of the reader |
| | 2146 | /// @{ |
| | 2147 | |
| | 2148 | /// \brief Start the batch processing |
| | 2149 | /// |
| | 2150 | /// This function starts the batch processing |
| | 2151 | void run() { |
| | 2152 | |
| | 2153 | LEMON_ASSERT(_is != 0, "This reader assigned to an other reader"); |
| | 2154 | |
| | 2155 | std::set<std::string> extra_sections; |
| | 2156 | |
| | 2157 | line_num = 0; |
| | 2158 | readLine(); |
| | 2159 | skipSection(); |
| | 2160 | |
| | 2161 | while (readSuccess()) { |
| | 2162 | try { |
| | 2163 | char c; |
| | 2164 | std::string section, caption; |
| | 2165 | line >> c; |
| | 2166 | _reader_bits::readToken(line, section); |
| | 2167 | _reader_bits::readToken(line, caption); |
| | 2168 | |
| | 2169 | if (line >> c) |
| | 2170 | throw DataFormatError("Extra character on the end of line"); |
| | 2171 | |
| | 2172 | if (extra_sections.find(section) != extra_sections.end()) { |
| | 2173 | std::ostringstream msg; |
| | 2174 | msg << "Multiple occurence of section " << section; |
| | 2175 | throw DataFormatError(msg.str().c_str()); |
| | 2176 | } |
| | 2177 | Sections::iterator it = _sections.find(section); |
| | 2178 | if (it != _sections.end()) { |
| | 2179 | extra_sections.insert(section); |
| | 2180 | it->second->process(*_is, line_num); |
| | 2181 | } |
| | 2182 | readLine(); |
| | 2183 | skipSection(); |
| | 2184 | } catch (DataFormatError& error) { |
| | 2185 | error.line(line_num); |
| | 2186 | throw; |
| | 2187 | } |
| | 2188 | } |
| | 2189 | for (Sections::iterator it = _sections.begin(); |
| | 2190 | it != _sections.end(); ++it) { |
| | 2191 | if (extra_sections.find(it->first) == extra_sections.end()) { |
| | 2192 | std::ostringstream os; |
| | 2193 | os << "Cannot find section: " << it->first; |
| | 2194 | throw DataFormatError(os.str().c_str()); |
| | 2195 | } |
| | 2196 | } |
| | 2197 | } |
| | 2198 | |
| | 2199 | /// @} |
| | 2200 | |
| | 2201 | |
| | 2202 | }; |