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 | | |
| 1977 | return tmp; |
| 1978 | } |
| 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); |