deba@1137: /* -*- C++ -*- deba@1137: * alpar@1956: * This file is a part of LEMON, a generic C++ optimization library alpar@1956: * alpar@2553: * Copyright (C) 2003-2008 alpar@1956: * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport alpar@1359: * (Egervary Research Group on Combinatorial Optimization, EGRES). deba@1137: * deba@1137: * Permission to use, modify and distribute this software is granted deba@1137: * provided that this copyright notice appears in all copies. For deba@1137: * precise terms see the accompanying LICENSE file. deba@1137: * deba@1137: * This software is provided "AS IS" with no warranty of any kind, deba@1137: * express or implied, and with no claim as to its suitability for any deba@1137: * purpose. deba@1137: * deba@1137: */ deba@1137: deba@2084: ///\ingroup lemon_io deba@1137: ///\file alpar@1287: ///\brief Lemon Graph Format reader. deba@1137: deba@1214: #ifndef LEMON_GRAPH_READER_H deba@1214: #define LEMON_GRAPH_READER_H deba@1214: deba@1137: #include deba@1137: deba@1137: #include deba@1408: #include deba@1137: deba@1137: namespace lemon { deba@1137: deba@2084: /// \addtogroup lemon_io deba@1333: /// @{ deba@1137: deba@1137: /// \brief The graph reader class. deba@1137: /// athos@1534: /// The \c GraphReader class provides the graph input. athos@1534: /// Before you read this documentation it might be useful to read the general athos@1534: /// description of \ref graph-io-page "Graph Input-Output". athos@1540: /// athos@2334: /// The file to be read may contain several maps and labeled athos@2334: /// (designated) nodes or edges. deba@1333: /// deba@1333: /// If you read a graph you need not read all the maps and items just those deba@1333: /// that you need. The interface of the \c GraphReader is very similar to deba@1333: /// the GraphWriter but the reading method does not depend on the order the athos@1540: /// given commands (i.e. you don't have to insist on the order in which the athos@1540: /// maps are given in the file). deba@1333: /// deba@2100: /// The reader object assumes that not read values do not contain deba@1333: /// whitespaces, therefore it has some extra possibilities to control how deba@1333: /// it should skip the values when the string representation contains spaces. deba@1333: /// alpar@1946: ///\code deba@1333: /// GraphReader reader(std::cin, graph); alpar@1946: ///\endcode deba@1333: /// deba@1394: /// The \c readNodeMap() function reads a map from the \c \@nodeset section. deba@1333: /// If there is a map that you do not want to read from the file and there is deba@1333: /// whitespace in the string represenation of the values then you should deba@1333: /// call the \c skipNodeMap() template member function with proper deba@1333: /// parameters. deba@1333: /// alpar@1946: ///\code deba@1421: /// reader.readNodeMap("coords", coords); deba@1333: /// deba@1901: /// reader.skipNodeMap("description", desc); deba@1333: /// deba@1394: /// reader.readNodeMap("color", colorMap); alpar@1946: ///\endcode deba@1333: /// deba@1394: /// With the \c readEdgeMap() member function you can give an edge map deba@1333: /// reading command similar to the NodeMaps. deba@1333: /// alpar@1946: ///\code deba@1394: /// reader.readEdgeMap("weight", weightMap); deba@1394: /// reader.readEdgeMap("label", labelMap); alpar@1946: ///\endcode deba@1333: /// deba@1408: /// With \c readNode() and \c readEdge() functions you can read deba@1408: /// labeled Nodes and Edges. deba@1333: /// alpar@1946: ///\code deba@1394: /// reader.readNode("source", sourceNode); deba@1394: /// reader.readNode("target", targetNode); deba@1333: /// deba@1394: /// reader.readEdge("observed", edge); alpar@1946: ///\endcode deba@1333: /// deba@1408: /// With the \c readAttribute() functions you can read an attribute athos@1540: /// into a variable. You can specify the reader for the attribute as deba@1408: /// the nodemaps. deba@1408: /// deba@1333: /// After you give all read commands you must call the \c run() member athos@1540: /// function, which executes all the commands. deba@1333: /// alpar@1946: ///\code deba@1333: /// reader.run(); alpar@1946: ///\endcode deba@1333: /// alpar@1287: /// \see DefaultReaderTraits alpar@1287: /// \see QuotedStringReader alpar@1138: /// \see \ref GraphWriter alpar@1138: /// \see \ref graph-io-page deba@1333: /// \author Balazs Dezso deba@1137: template deba@1137: class GraphReader { deba@1137: public: deba@1137: deba@1137: typedef _Graph Graph; deba@1137: typedef typename Graph::Node Node; deba@1137: typedef typename Graph::Edge Edge; deba@1137: deba@1137: typedef _ReaderTraits ReaderTraits; deba@1408: typedef typename ReaderTraits::Skipper DefaultSkipper; deba@1137: deba@1137: /// \brief Construct a new GraphReader. deba@1137: /// deba@1208: /// Construct a new GraphReader. It reads into the given graph athos@1540: /// and it uses the given reader as the default skipper. deba@1705: GraphReader(std::istream& _is, Graph& _graph, deba@1408: const DefaultSkipper& _skipper = DefaultSkipper()) deba@1421: : reader(new LemonReader(_is)), own_reader(true), skipper(_skipper), deba@1421: nodeset_reader(*reader, _graph, std::string(), skipper), deba@1421: edgeset_reader(*reader, _graph, nodeset_reader, deba@1421: std::string(), skipper), deba@1408: node_reader(*reader, nodeset_reader, std::string()), deba@1408: edge_reader(*reader, edgeset_reader, std::string()), deba@1408: attribute_reader(*reader, std::string()) {} deba@1408: deba@1408: /// \brief Construct a new GraphReader. deba@1408: /// deba@1408: /// Construct a new GraphReader. It reads into the given graph athos@1540: /// and it uses the given reader as the default skipper. deba@1705: GraphReader(const std::string& _filename, Graph& _graph, deba@1408: const DefaultSkipper& _skipper = DefaultSkipper()) deba@1408: : reader(new LemonReader(_filename)), own_reader(true), deba@1421: skipper(_skipper), deba@1421: nodeset_reader(*reader, _graph, std::string(), skipper), deba@1421: edgeset_reader(*reader, _graph, nodeset_reader, deba@1421: std::string(), skipper), deba@1408: node_reader(*reader, nodeset_reader, std::string()), deba@1408: edge_reader(*reader, edgeset_reader, std::string()), deba@1408: attribute_reader(*reader, std::string()) {} deba@1408: deba@1408: /// \brief Construct a new GraphReader. deba@1408: /// deba@1408: /// Construct a new GraphReader. It reads into the given graph athos@1540: /// and it uses the given reader as the default skipper. deba@1705: GraphReader(LemonReader& _reader, Graph& _graph, deba@1408: const DefaultSkipper& _skipper = DefaultSkipper()) deba@1421: : reader(_reader), own_reader(false), skipper(_skipper), deba@1421: nodeset_reader(*reader, _graph, std::string(), skipper), deba@1421: edgeset_reader(*reader, _graph, nodeset_reader, deba@1421: std::string(), skipper), deba@1408: node_reader(*reader, nodeset_reader, std::string()), deba@1408: edge_reader(*reader, edgeset_reader, std::string()), deba@1408: attribute_reader(*reader, std::string()) {} deba@1137: deba@1137: /// \brief Destruct the graph reader. deba@1137: /// deba@1137: /// Destruct the graph reader. deba@1137: ~GraphReader() { deba@1408: if (own_reader) deba@1408: delete reader; deba@1137: } deba@1137: athos@1540: /// \brief Give a new node map reading command to the reader. deba@1137: /// athos@1540: /// Give a new node map reading command to the reader. deba@1137: template deba@1394: GraphReader& readNodeMap(std::string name, Map& map) { deba@1421: nodeset_reader.readNodeMap(name, map); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: template deba@1421: GraphReader& readNodeMap(std::string name, const Map& map) { deba@1421: nodeset_reader.readNodeMap(name, map); deba@1408: return *this; deba@1137: } deba@1137: athos@1540: /// \brief Give a new node map reading command to the reader. deba@1137: /// athos@1540: /// Give a new node map reading command to the reader. deba@2386: template deba@1394: GraphReader& readNodeMap(std::string name, Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: nodeset_reader.readNodeMap(name, map, ir); deba@1421: return *this; deba@1421: } deba@1421: deba@2386: template deba@1421: GraphReader& readNodeMap(std::string name, const Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: nodeset_reader.readNodeMap(name, map, ir); deba@1137: return *this; deba@1137: } deba@1137: athos@1540: /// \brief Give a new node map skipping command to the reader. deba@1137: /// athos@1540: /// Give a new node map skipping command to the reader. deba@2386: template deba@1137: GraphReader& skipNodeMap(std::string name, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: nodeset_reader.skipNodeMap(name, ir); deba@1137: return *this; deba@1137: } deba@1137: athos@1540: /// \brief Give a new edge map reading command to the reader. deba@1137: /// athos@1540: /// Give a new edge map reading command to the reader. deba@1137: template deba@1394: GraphReader& readEdgeMap(std::string name, Map& map) { deba@1421: edgeset_reader.readEdgeMap(name, map); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: template deba@1421: GraphReader& readEdgeMap(std::string name, const Map& map) { deba@1421: edgeset_reader.readEdgeMap(name, map); deba@1408: return *this; deba@1137: } deba@1137: deba@1137: athos@1540: /// \brief Give a new edge map reading command to the reader. deba@1137: /// athos@1540: /// Give a new edge map reading command to the reader. deba@2386: template deba@1394: GraphReader& readEdgeMap(std::string name, Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: edgeset_reader.readEdgeMap(name, map, ir); deba@1421: return *this; deba@1421: } deba@1421: deba@2386: template deba@1421: GraphReader& readEdgeMap(std::string name, const Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: edgeset_reader.readEdgeMap(name, map, ir); deba@1137: return *this; deba@1137: } deba@1137: athos@1540: /// \brief Give a new edge map skipping command to the reader. deba@1137: /// athos@1540: /// Give a new edge map skipping command to the reader. deba@2386: template deba@1421: GraphReader& skipEdgeMap(std::string name, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: edgeset_reader.skipEdgeMap(name, ir); deba@1137: return *this; deba@1137: } deba@1137: athos@1540: /// \brief Give a new labeled node reading command to the reader. deba@1137: /// athos@1540: /// Give a new labeled node reading command to the reader. deba@1394: GraphReader& readNode(std::string name, Node& node) { deba@1408: node_reader.readNode(name, node); deba@1137: return *this; deba@1137: } deba@1137: athos@1540: /// \brief Give a new labeled edge reading command to the reader. deba@1137: /// athos@1540: /// Give a new labeled edge reading command to the reader. deba@1394: GraphReader& readEdge(std::string name, Edge& edge) { deba@1408: edge_reader.readEdge(name, edge); deba@1476: return *this; deba@1408: } deba@1408: athos@1540: /// \brief Give a new attribute reading command. deba@1408: /// athos@1540: /// Give a new attribute reading command. deba@1408: template deba@1408: GraphReader& readAttribute(std::string name, Value& value) { deba@1408: attribute_reader.readAttribute(name, value); deba@1137: return *this; deba@1137: } deba@1408: athos@1540: /// \brief Give a new attribute reading command. deba@1408: /// athos@1540: /// Give a new attribute reading command. deba@2386: template deba@1408: GraphReader& readAttribute(std::string name, Value& value, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: attribute_reader.readAttribute(name, value, ir); deba@1408: return *this; deba@1408: } deba@1408: deba@1408: /// \brief Conversion operator to LemonReader. deba@1408: /// athos@1540: /// Conversion operator to LemonReader. It makes possible to access the athos@1540: /// encapsulated \e LemonReader, this way you can attach to this reader athos@1540: /// new instances of \e LemonReader::SectionReader. For more details see athos@1540: /// the \ref rwbackground "Background of Reading and Writing". deba@1408: operator LemonReader&() { deba@1408: return *reader; deba@1408: } deba@1137: athos@1540: /// \brief Executes the reading commands. deba@1137: /// athos@1540: /// Executes the reading commands. deba@1137: void run() { deba@1408: reader->run(); deba@1396: } deba@1396: deba@1901: deba@1901: /// \brief Returns true if the reader can give back the items by its label. deba@1429: /// deba@1901: /// \brief Returns true if the reader can give back the items by its label. deba@1901: bool isLabelReader() const { deba@1901: return nodeset_reader.isLabelReader() && edgeset_reader.isLabelReader(); deba@1901: } deba@1901: deba@1901: /// \brief Gives back the node by its label. deba@1901: /// deba@1901: /// It reads an label from the stream and gives back which node belongs to alpar@1935: /// it. It is possible only if there was read a "label" named node map. deba@1901: void readLabel(std::istream& is, Node& node) const { deba@1901: nodeset_reader.readLabel(is, node); deba@1429: } deba@1429: deba@1901: /// \brief Gives back the edge by its label. deba@1429: /// deba@1901: /// It reads an label from the stream and gives back which edge belongs to alpar@1935: /// it. It is possible only if there was read a "label" named edge map. deba@1901: void readLabel(std::istream& is, Edge& edge) const { deba@2467: edgeset_reader.readLabel(is, edge); deba@1429: } deba@1429: deba@1137: private: deba@1137: deba@1408: LemonReader* reader; deba@1408: bool own_reader; deba@1137: deba@1408: DefaultSkipper skipper; deba@1137: deba@1408: NodeSetReader nodeset_reader; deba@1408: EdgeSetReader edgeset_reader; deba@1408: deba@1408: NodeReader node_reader; deba@1408: EdgeReader edge_reader; deba@1408: deba@1408: AttributeReader attribute_reader; deba@1137: }; deba@1137: athos@1534: deba@1744: /// \brief The undirected graph reader class. deba@1421: /// klao@1909: /// The \c UGraphReader class provides the graph input. athos@1540: /// Before you read this documentation it might be useful to read the general athos@1540: /// description of \ref graph-io-page "Graph Input-Output". athos@1540: /// deba@1421: /// The given file format may contain several maps and labeled nodes or deba@1421: /// edges. deba@1421: /// deba@1421: /// If you read a graph you need not read all the maps and items just those klao@1909: /// that you need. The interface of the \c UGraphReader is very similar klao@1909: /// to the UGraphWriter but the reading method does not depend on the athos@1540: /// order of the given commands. deba@1421: /// deba@2100: /// The reader object suppose that each not read value does not contain deba@1421: /// whitespaces, therefore it has some extra possibilities to control how deba@1421: /// it should skip the values when the string representation contains spaces. deba@1421: /// alpar@1946: ///\code klao@1909: /// UGraphReader reader(std::cin, graph); alpar@1946: ///\endcode deba@1421: /// deba@1421: /// The \c readNodeMap() function reads a map from the \c \@nodeset section. deba@1421: /// If there is a map that you do not want to read from the file and there is deba@1421: /// whitespace in the string represenation of the values then you should deba@1421: /// call the \c skipNodeMap() template member function with proper deba@1421: /// parameters. deba@1421: /// alpar@1946: ///\code deba@1421: /// reader.readNodeMap("coords", coords); deba@1421: /// deba@1901: /// reader.skipNodeMap("description", desc); deba@1421: /// deba@1421: /// reader.readNodeMap("color", colorMap); alpar@1946: ///\endcode deba@1421: /// klao@1909: /// With the \c readUEdgeMap() member function you can give an klao@1909: /// uedge map reading command similar to the NodeMaps. deba@1421: /// alpar@1946: ///\code klao@1909: /// reader.readUEdgeMap("capacity", capacityMap); alpar@1946: ///\endcode deba@1421: /// deba@1421: /// The reading of the directed edge maps is just a syntactical sugar. deba@1421: /// It reads two undirected edgemaps into a directed edge map. The deba@1421: /// undirected edge maps' name should be start with the \c '+' and the deba@1421: /// \c '-' character and the same. deba@1421: /// alpar@1946: ///\code deba@1421: /// reader.readEdgeMap("flow", flowMap); alpar@1946: ///\endcode deba@1421: /// klao@1909: /// With \c readNode() and \c readUEdge() functions you can read klao@1909: /// labeled Nodes and UEdges. deba@1421: /// alpar@1946: ///\code deba@1421: /// reader.readNode("source", sourceNode); deba@1421: /// reader.readNode("target", targetNode); deba@1421: /// klao@1909: /// reader.readUEdge("observed", uEdge); alpar@1946: ///\endcode deba@1421: /// deba@1421: /// With the \c readAttribute() functions you can read an attribute deba@1421: /// in a variable. You can specify the reader for the attribute as deba@1421: /// the nodemaps. deba@1421: /// deba@1421: /// After you give all read commands you must call the \c run() member deba@1421: /// function, which execute all the commands. deba@1421: /// alpar@1946: ///\code deba@1421: /// reader.run(); alpar@1946: ///\endcode deba@1421: /// deba@1421: /// \see GraphReader deba@1421: /// \see DefaultReaderTraits klao@1909: /// \see \ref UGraphWriter deba@1421: /// \see \ref graph-io-page deba@1421: /// deba@1421: /// \author Balazs Dezso deba@1421: template klao@1909: class UGraphReader { deba@1421: public: deba@1421: deba@1421: typedef _Graph Graph; deba@1421: typedef typename Graph::Node Node; deba@1421: typedef typename Graph::Edge Edge; klao@1909: typedef typename Graph::UEdge UEdge; deba@1421: deba@1421: typedef _ReaderTraits ReaderTraits; deba@1421: typedef typename ReaderTraits::Skipper DefaultSkipper; deba@1421: klao@1909: /// \brief Construct a new UGraphReader. deba@1421: /// klao@1909: /// Construct a new UGraphReader. It reads into the given graph deba@1421: /// and it use the given reader as the default skipper. klao@1909: UGraphReader(std::istream& _is, Graph& _graph, deba@1421: const DefaultSkipper& _skipper = DefaultSkipper()) deba@1421: : reader(new LemonReader(_is)), own_reader(true), skipper(_skipper), deba@1421: nodeset_reader(*reader, _graph, std::string(), skipper), deba@2467: uedgeset_reader(*reader, _graph, nodeset_reader, deba@1421: std::string(), skipper), deba@1421: node_reader(*reader, nodeset_reader, std::string()), deba@2467: uedge_reader(*reader, uedgeset_reader, std::string()), deba@1421: attribute_reader(*reader, std::string()) {} deba@1421: klao@1909: /// \brief Construct a new UGraphReader. deba@1421: /// klao@1909: /// Construct a new UGraphReader. It reads into the given graph deba@1421: /// and it use the given reader as the default skipper. klao@1909: UGraphReader(const std::string& _filename, Graph& _graph, deba@1421: const DefaultSkipper& _skipper = DefaultSkipper()) deba@1421: : reader(new LemonReader(_filename)), own_reader(true), deba@1421: skipper(_skipper), deba@1421: nodeset_reader(*reader, _graph, std::string(), skipper), deba@2467: uedgeset_reader(*reader, _graph, nodeset_reader, deba@1421: std::string(), skipper), deba@1421: node_reader(*reader, nodeset_reader, std::string()), deba@2467: uedge_reader(*reader, uedgeset_reader, std::string()), deba@1421: attribute_reader(*reader, std::string()) {} deba@1421: klao@1909: /// \brief Construct a new UGraphReader. deba@1421: /// klao@1909: /// Construct a new UGraphReader. It reads into the given graph deba@1421: /// and it use the given reader as the default skipper. klao@1909: UGraphReader(LemonReader& _reader, Graph& _graph, deba@1421: const DefaultSkipper& _skipper = DefaultSkipper()) deba@1421: : reader(_reader), own_reader(false), skipper(_skipper), deba@1421: nodeset_reader(*reader, _graph, std::string(), skipper), deba@2467: uedgeset_reader(*reader, _graph, nodeset_reader, deba@1421: std::string(), skipper), deba@1421: node_reader(*reader, nodeset_reader, std::string()), deba@2467: uedge_reader(*reader, uedgeset_reader, std::string()), deba@1421: attribute_reader(*reader, std::string()) {} deba@1421: deba@1421: /// \brief Destruct the graph reader. deba@1421: /// deba@1421: /// Destruct the graph reader. klao@1909: ~UGraphReader() { deba@1421: if (own_reader) deba@1421: delete reader; deba@1421: } deba@1421: athos@1540: /// \brief Give a new node map reading command to the reader. deba@1421: /// athos@1540: /// Give a new node map reading command to the reader. deba@1421: template klao@1909: UGraphReader& readNodeMap(std::string name, Map& map) { deba@1421: nodeset_reader.readNodeMap(name, map); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: template klao@1909: UGraphReader& readNodeMap(std::string name, const Map& map) { deba@1421: nodeset_reader.readNodeMap(name, map); deba@1421: return *this; deba@1421: } deba@1421: athos@1540: /// \brief Give a new node map reading command to the reader. deba@1421: /// athos@1540: /// Give a new node map reading command to the reader. deba@2386: template klao@1909: UGraphReader& readNodeMap(std::string name, Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: nodeset_reader.readNodeMap(name, map, ir); deba@1421: return *this; deba@1421: } deba@1421: deba@2386: template klao@1909: UGraphReader& readNodeMap(std::string name, const Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: nodeset_reader.readNodeMap(name, map, ir); deba@1421: return *this; deba@1421: } deba@1421: athos@1540: /// \brief Give a new node map skipping command to the reader. deba@1421: /// athos@1540: /// Give a new node map skipping command to the reader. deba@2386: template klao@1909: UGraphReader& skipNodeMap(std::string name, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: nodeset_reader.skipNodeMap(name, ir); deba@1421: return *this; deba@1421: } deba@1421: athos@1540: /// \brief Give a new undirected edge map reading command to the reader. deba@1421: /// athos@1540: /// Give a new undirected edge map reading command to the reader. deba@1421: template klao@1909: UGraphReader& readUEdgeMap(std::string name, Map& map) { deba@2467: uedgeset_reader.readUEdgeMap(name, map); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: template klao@1909: UGraphReader& readUEdgeMap(std::string name, const Map& map) { deba@2467: uedgeset_reader.readUEdgeMap(name, map); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: athos@1540: /// \brief Give a new undirected edge map reading command to the reader. deba@1421: /// athos@1540: /// Give a new undirected edge map reading command to the reader. deba@2386: template klao@1909: UGraphReader& readUEdgeMap(std::string name, Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2467: uedgeset_reader.readUEdgeMap(name, map, ir); deba@1421: return *this; deba@1421: } deba@1421: deba@2386: template klao@1909: UGraphReader& readUEdgeMap(std::string name, const Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2467: uedgeset_reader.readUEdgeMap(name, map, ir); deba@1421: return *this; deba@1421: } deba@1421: athos@1540: /// \brief Give a new undirected edge map skipping command to the reader. deba@1421: /// athos@1540: /// Give a new undirected edge map skipping command to the reader. deba@2386: template klao@1909: UGraphReader& skipUEdgeMap(std::string name, deba@2386: const ItemReader& ir = ItemReader()) { deba@2467: uedgeset_reader.skipUMap(name, ir); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: athos@1540: /// \brief Give a new edge map reading command to the reader. deba@1421: /// athos@1540: /// Give a new edge map reading command to the reader. deba@1421: template klao@1909: UGraphReader& readEdgeMap(std::string name, Map& map) { deba@2467: uedgeset_reader.readEdgeMap(name, map); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: template klao@1909: UGraphReader& readEdgeMap(std::string name, const Map& map) { deba@2467: uedgeset_reader.readEdgeMap(name, map); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: athos@1540: /// \brief Give a new edge map reading command to the reader. deba@1421: /// athos@1540: /// Give a new edge map reading command to the reader. deba@2386: template klao@1909: UGraphReader& readEdgeMap(std::string name, Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2467: uedgeset_reader.readEdgeMap(name, map, ir); deba@1421: return *this; deba@1421: } deba@1421: deba@2386: template klao@1909: UGraphReader& readEdgeMap(std::string name, const Map& map, deba@2386: const ItemReader& ir = ItemReader()) { deba@2467: uedgeset_reader.readEdgeMap(name, map, ir); deba@1421: return *this; deba@1421: } deba@1421: athos@1540: /// \brief Give a new edge map skipping command to the reader. deba@1421: /// athos@1540: /// Give a new edge map skipping command to the reader. deba@2386: template klao@1909: UGraphReader& skipEdgeMap(std::string name, deba@2386: const ItemReader& ir = ItemReader()) { deba@2467: uedgeset_reader.skipEdgeMap(name, ir); deba@1421: return *this; deba@1421: } deba@1421: athos@1540: /// \brief Give a new labeled node reading command to the reader. deba@1421: /// athos@1540: /// Give a new labeled node reading command to the reader. klao@1909: UGraphReader& readNode(std::string name, Node& node) { deba@1421: node_reader.readNode(name, node); deba@1421: return *this; deba@1421: } deba@1421: athos@1540: /// \brief Give a new labeled edge reading command to the reader. deba@1421: /// athos@1540: /// Give a new labeled edge reading command to the reader. klao@1909: UGraphReader& readEdge(std::string name, Edge& edge) { deba@2467: uedge_reader.readEdge(name, edge); deba@1429: } deba@1429: athos@1540: /// \brief Give a new labeled undirected edge reading command to the athos@1540: /// reader. deba@1429: /// athos@1540: /// Give a new labeled undirected edge reading command to the reader. klao@1909: UGraphReader& readUEdge(std::string name, UEdge& edge) { deba@2467: uedge_reader.readUEdge(name, edge); deba@1421: } deba@1421: athos@1540: /// \brief Give a new attribute reading command. deba@1421: /// athos@1540: /// Give a new attribute reading command. deba@1421: template klao@1909: UGraphReader& readAttribute(std::string name, Value& value) { deba@1421: attribute_reader.readAttribute(name, value); deba@1421: return *this; deba@1421: } deba@1421: athos@1540: /// \brief Give a new attribute reading command. deba@1421: /// athos@1540: /// Give a new attribute reading command. deba@2386: template klao@1909: UGraphReader& readAttribute(std::string name, Value& value, deba@2386: const ItemReader& ir = ItemReader()) { deba@2386: attribute_reader.readAttribute(name, value, ir); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: /// \brief Conversion operator to LemonReader. deba@1421: /// deba@1421: /// Conversion operator to LemonReader. It make possible deba@1421: /// to access the encapsulated \e LemonReader, this way deba@1421: /// you can attach to this reader new instances of deba@1421: /// \e LemonReader::SectionReader. deba@1421: operator LemonReader&() { deba@1421: return *reader; deba@1421: } deba@1421: athos@1540: /// \brief Executes the reading commands. deba@1421: /// athos@1540: /// Executes the reading commands. deba@1421: void run() { deba@1421: reader->run(); deba@1421: } deba@1421: deba@1901: deba@1901: /// \brief Returns true if the reader can give back the items by its label. deba@1429: /// deba@2467: /// Returns true if the reader can give back the items by its label. deba@1901: bool isLabelReader() const { deba@1901: return nodeset_reader.isLabelReader() && deba@2467: uedgeset_reader.isLabelReader(); deba@1901: } deba@1901: deba@1901: /// \brief Gives back the node by its label. deba@1901: /// deba@1901: /// It reads an label from the stream and gives back which node belongs to alpar@1935: /// it. It is possible only if there was read a "label" named node map. deba@1901: void readLabel(std::istream& is, Node& node) const { deba@1901: return nodeset_reader.readLabel(is, node); deba@1429: } deba@1429: alpar@1935: /// \brief Gives back the edge by its label deba@1429: /// deba@1901: /// It reads an label from the stream and gives back which edge belongs to alpar@1935: /// it. It is possible only if there was read a "label" named edge map. deba@1901: void readLabel(std::istream& is, Edge& edge) const { deba@2467: return uedgeset_reader.readLabel(is, edge); deba@1429: } deba@1429: deba@1901: /// \brief Gives back the undirected edge by its label. deba@1429: /// deba@1901: /// It reads an label from the stream and gives back which undirected edge alpar@1935: /// belongs to it. It is possible only if there was read a "label" named deba@1429: /// edge map. klao@1909: void readLabel(std::istream& is, UEdge& uedge) const { deba@2467: return uedgeset_reader.readLabel(is, uedge); deba@1429: } deba@1429: deba@1429: deba@1421: private: deba@1421: deba@1421: LemonReader* reader; deba@1421: bool own_reader; deba@1421: deba@1421: DefaultSkipper skipper; deba@1421: deba@1421: NodeSetReader nodeset_reader; deba@2467: UEdgeSetReader uedgeset_reader; deba@1421: deba@1421: NodeReader node_reader; deba@2467: UEdgeReader uedge_reader; deba@1421: deba@1421: AttributeReader attribute_reader; deba@1421: }; deba@1421: deba@2502: /// \brief The bipartite graph reader class. deba@2502: /// deba@2502: /// The \c BpUGraphReader class provides the graph input. deba@2502: /// Before you read this documentation it might be useful to read the general deba@2502: /// description of \ref graph-io-page "Graph Input-Output". deba@2502: /// deba@2502: /// The given file format may contain several maps and labeled nodes or deba@2502: /// edges. deba@2502: /// deba@2502: /// If you read a graph you need not read all the maps and items just those deba@2502: /// that you need. The interface of the \c BpUGraphReader is very similar deba@2502: /// to the BpUGraphWriter but the reading method does not depend on the deba@2502: /// order of the given commands. deba@2502: /// deba@2502: /// The reader object suppose that each not read value does not contain deba@2502: /// whitespaces, therefore it has some extra possibilities to control how deba@2502: /// it should skip the values when the string representation contains spaces. deba@2502: /// deba@2502: ///\code deba@2502: /// BpUGraphReader reader(std::cin, graph); deba@2502: ///\endcode deba@2502: /// deba@2502: /// The \c readANodeMap() function reads a map from the A-part of deba@2502: /// the\c \@bpnodeset section, while the \c readBNodeMap() reads deba@2502: /// from the B-part of the section. If you use the \c readNodeMap() deba@2502: /// function, then the given map should appear in both part of the deba@2502: /// section. If there is a map that you do not want to read from the deba@2502: /// file and there is whitespace in the string represenation of the deba@2502: /// values then you should call the \c skipANodeMap(), \c deba@2502: /// skipBNodeMap() or \c skipNodeMap() template member function with deba@2502: /// proper parameters. deba@2502: /// deba@2502: ///\code deba@2502: /// reader.readNodeMap("coords", coords); deba@2502: /// reader.readANodeMap("range", range); deba@2502: /// reader.readANodeMap("benefit", benefit); deba@2502: /// deba@2502: /// reader.skipNodeMap("description", desc); deba@2502: /// deba@2502: /// reader.readNodeMap("color", colorMap); deba@2502: ///\endcode deba@2502: /// deba@2502: /// With the \c readUEdgeMap() member function you can give an deba@2502: /// uedge map reading command similar to the NodeMaps. deba@2502: /// deba@2502: ///\code deba@2502: /// reader.readUEdgeMap("capacity", capacityMap); deba@2502: /// reader.readEdgeMap("flow", flowMap); deba@2502: ///\endcode deba@2502: /// deba@2502: /// With \c readNode() and \c readUEdge() functions you can read deba@2502: /// labeled Nodes and UEdges. deba@2502: /// deba@2502: ///\code deba@2502: /// reader.readNode("source", sourceNode); deba@2502: /// reader.readNode("target", targetNode); deba@2502: /// deba@2502: /// reader.readUEdge("observed", uEdge); deba@2502: ///\endcode deba@2502: /// deba@2502: /// With the \c readAttribute() functions you can read an attribute deba@2502: /// in a variable. You can specify the reader for the attribute as deba@2502: /// the nodemaps. deba@2502: /// deba@2502: /// After you give all read commands you must call the \c run() member deba@2502: /// function, which execute all the commands. deba@2502: /// deba@2502: ///\code deba@2502: /// reader.run(); deba@2502: ///\endcode deba@2502: /// deba@2502: /// \see GraphReader deba@2502: /// \see DefaultReaderTraits deba@2502: /// \see \ref UGraphWriter deba@2502: /// \see \ref graph-io-page deba@2502: /// deba@2502: /// \author Balazs Dezso deba@2502: template deba@2502: class BpUGraphReader { deba@2502: public: deba@2502: deba@2502: typedef _Graph Graph; deba@2502: typedef typename Graph::Node Node; deba@2502: typedef typename Graph::Edge Edge; deba@2502: typedef typename Graph::UEdge UEdge; deba@2502: deba@2502: typedef _ReaderTraits ReaderTraits; deba@2502: typedef typename ReaderTraits::Skipper DefaultSkipper; deba@2502: deba@2502: /// \brief Construct a new BpUGraphReader. deba@2502: /// deba@2502: /// Construct a new BpUGraphReader. It reads into the given graph deba@2502: /// and it use the given reader as the default skipper. deba@2502: BpUGraphReader(std::istream& _is, Graph& _graph, deba@2502: const DefaultSkipper& _skipper = DefaultSkipper()) deba@2502: : reader(new LemonReader(_is)), own_reader(true), skipper(_skipper), deba@2502: nodeset_reader(*reader, _graph, std::string(), skipper), deba@2502: uedgeset_reader(*reader, _graph, nodeset_reader, deba@2502: std::string(), skipper), deba@2502: node_reader(*reader, nodeset_reader, std::string()), deba@2502: uedge_reader(*reader, uedgeset_reader, std::string()), deba@2502: attribute_reader(*reader, std::string()) {} deba@2502: deba@2502: /// \brief Construct a new BpUGraphReader. deba@2502: /// deba@2502: /// Construct a new BpUGraphReader. It reads into the given graph deba@2502: /// and it use the given reader as the default skipper. deba@2502: BpUGraphReader(const std::string& _filename, Graph& _graph, deba@2502: const DefaultSkipper& _skipper = DefaultSkipper()) deba@2502: : reader(new LemonReader(_filename)), own_reader(true), deba@2502: skipper(_skipper), deba@2502: nodeset_reader(*reader, _graph, std::string(), skipper), deba@2502: uedgeset_reader(*reader, _graph, nodeset_reader, deba@2502: std::string(), skipper), deba@2502: node_reader(*reader, nodeset_reader, std::string()), deba@2502: uedge_reader(*reader, uedgeset_reader, std::string()), deba@2502: attribute_reader(*reader, std::string()) {} deba@2502: deba@2502: /// \brief Construct a new BpUGraphReader. deba@2502: /// deba@2502: /// Construct a new BpUGraphReader. It reads into the given graph deba@2502: /// and it use the given reader as the default skipper. deba@2502: BpUGraphReader(LemonReader& _reader, Graph& _graph, deba@2502: const DefaultSkipper& _skipper = DefaultSkipper()) deba@2502: : reader(_reader), own_reader(false), skipper(_skipper), deba@2502: nodeset_reader(*reader, _graph, std::string(), skipper), deba@2502: uedgeset_reader(*reader, _graph, nodeset_reader, deba@2502: std::string(), skipper), deba@2502: node_reader(*reader, nodeset_reader, std::string()), deba@2502: uedge_reader(*reader, uedgeset_reader, std::string()), deba@2502: attribute_reader(*reader, std::string()) {} deba@2502: deba@2502: /// \brief Destruct the graph reader. deba@2502: /// deba@2502: /// Destruct the graph reader. deba@2502: ~BpUGraphReader() { deba@2502: if (own_reader) deba@2502: delete reader; deba@2502: } deba@2502: deba@2502: /// \brief Give a new node map reading command to the reader. deba@2502: /// deba@2502: /// Give a new node map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readNodeMap(std::string name, Map& map) { deba@2502: nodeset_reader.readNodeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readNodeMap(std::string name, const Map& map) { deba@2502: nodeset_reader.readNodeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new node map reading command to the reader. deba@2502: /// deba@2502: /// Give a new node map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readNodeMap(std::string name, Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: nodeset_reader.readNodeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readNodeMap(std::string name, const Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: nodeset_reader.readNodeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new node map skipping command to the reader. deba@2502: /// deba@2502: /// Give a new node map skipping command to the reader. deba@2502: template deba@2502: BpUGraphReader& skipNodeMap(std::string name, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: nodeset_reader.skipNodeMap(name, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new A-node map reading command to the reader. deba@2502: /// deba@2502: /// Give a new A-node map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readANodeMap(std::string name, Map& map) { deba@2502: nodeset_reader.readANodeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readANodeMap(std::string name, const Map& map) { deba@2502: nodeset_reader.readANodeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new A-node map reading command to the reader. deba@2502: /// deba@2502: /// Give a new A-node map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readANodeMap(std::string name, Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: nodeset_reader.readANodeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readANodeMap(std::string name, const Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: nodeset_reader.readNodeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new A-node map skipping command to the reader. deba@2502: /// deba@2502: /// Give a new A-node map skipping command to the reader. deba@2502: template deba@2502: BpUGraphReader& skipANodeMap(std::string name, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: nodeset_reader.skipANodeMap(name, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new B-node map reading command to the reader. deba@2502: /// deba@2502: /// Give a new B-node map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readBNodeMap(std::string name, Map& map) { deba@2502: nodeset_reader.readBNodeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readBNodeMap(std::string name, const Map& map) { deba@2502: nodeset_reader.readBNodeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new B-node map reading command to the reader. deba@2502: /// deba@2502: /// Give a new B-node map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readBNodeMap(std::string name, Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: nodeset_reader.readBNodeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readBNodeMap(std::string name, const Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: nodeset_reader.readNodeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new B-node map skipping command to the reader. deba@2502: /// deba@2502: /// Give a new B-node map skipping command to the reader. deba@2502: template deba@2502: BpUGraphReader& skipBNodeMap(std::string name, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: nodeset_reader.skipBNodeMap(name, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new undirected edge map reading command to the reader. deba@2502: /// deba@2502: /// Give a new undirected edge map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readUEdgeMap(std::string name, Map& map) { deba@2502: uedgeset_reader.readUEdgeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readUEdgeMap(std::string name, const Map& map) { deba@2502: uedgeset_reader.readUEdgeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: deba@2502: /// \brief Give a new undirected edge map reading command to the reader. deba@2502: /// deba@2502: /// Give a new undirected edge map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readUEdgeMap(std::string name, Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: uedgeset_reader.readUEdgeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readUEdgeMap(std::string name, const Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: uedgeset_reader.readUEdgeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new undirected edge map skipping command to the reader. deba@2502: /// deba@2502: /// Give a new undirected edge map skipping command to the reader. deba@2502: template deba@2502: BpUGraphReader& skipUEdgeMap(std::string name, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: uedgeset_reader.skipUMap(name, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: deba@2502: /// \brief Give a new edge map reading command to the reader. deba@2502: /// deba@2502: /// Give a new edge map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readEdgeMap(std::string name, Map& map) { deba@2502: uedgeset_reader.readEdgeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readEdgeMap(std::string name, const Map& map) { deba@2502: uedgeset_reader.readEdgeMap(name, map); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: deba@2502: /// \brief Give a new edge map reading command to the reader. deba@2502: /// deba@2502: /// Give a new edge map reading command to the reader. deba@2502: template deba@2502: BpUGraphReader& readEdgeMap(std::string name, Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: uedgeset_reader.readEdgeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: template deba@2502: BpUGraphReader& readEdgeMap(std::string name, const Map& map, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: uedgeset_reader.readEdgeMap(name, map, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new edge map skipping command to the reader. deba@2502: /// deba@2502: /// Give a new edge map skipping command to the reader. deba@2502: template deba@2502: BpUGraphReader& skipEdgeMap(std::string name, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: uedgeset_reader.skipEdgeMap(name, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new labeled node reading command to the reader. deba@2502: /// deba@2502: /// Give a new labeled node reading command to the reader. deba@2502: BpUGraphReader& readNode(std::string name, Node& node) { deba@2502: node_reader.readNode(name, node); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new labeled edge reading command to the reader. deba@2502: /// deba@2502: /// Give a new labeled edge reading command to the reader. deba@2502: BpUGraphReader& readEdge(std::string name, Edge& edge) { deba@2502: uedge_reader.readEdge(name, edge); deba@2502: } deba@2502: deba@2502: /// \brief Give a new labeled undirected edge reading command to the deba@2502: /// reader. deba@2502: /// deba@2502: /// Give a new labeled undirected edge reading command to the reader. deba@2502: BpUGraphReader& readUEdge(std::string name, UEdge& edge) { deba@2502: uedge_reader.readUEdge(name, edge); deba@2502: } deba@2502: deba@2502: /// \brief Give a new attribute reading command. deba@2502: /// deba@2502: /// Give a new attribute reading command. deba@2502: template deba@2502: BpUGraphReader& readAttribute(std::string name, Value& value) { deba@2502: attribute_reader.readAttribute(name, value); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Give a new attribute reading command. deba@2502: /// deba@2502: /// Give a new attribute reading command. deba@2502: template deba@2502: BpUGraphReader& readAttribute(std::string name, Value& value, deba@2502: const ItemReader& ir = ItemReader()) { deba@2502: attribute_reader.readAttribute(name, value, ir); deba@2502: return *this; deba@2502: } deba@2502: deba@2502: /// \brief Conversion operator to LemonReader. deba@2502: /// deba@2502: /// Conversion operator to LemonReader. It make possible deba@2502: /// to access the encapsulated \e LemonReader, this way deba@2502: /// you can attach to this reader new instances of deba@2502: /// \e LemonReader::SectionReader. deba@2502: operator LemonReader&() { deba@2502: return *reader; deba@2502: } deba@2502: deba@2502: /// \brief Executes the reading commands. deba@2502: /// deba@2502: /// Executes the reading commands. deba@2502: void run() { deba@2502: reader->run(); deba@2502: } deba@2502: deba@2502: deba@2502: /// \brief Returns true if the reader can give back the items by its label. deba@2502: /// deba@2502: /// Returns true if the reader can give back the items by its label. deba@2502: bool isLabelReader() const { deba@2502: return nodeset_reader.isLabelReader() && deba@2502: uedgeset_reader.isLabelReader(); deba@2502: } deba@2502: deba@2502: /// \brief Gives back the node by its label. deba@2502: /// deba@2502: /// It reads an label from the stream and gives back which node belongs to deba@2502: /// it. It is possible only if there was read a "label" named node map. deba@2502: void readLabel(std::istream& is, Node& node) const { deba@2502: return nodeset_reader.readLabel(is, node); deba@2502: } deba@2502: deba@2502: /// \brief Gives back the edge by its label deba@2502: /// deba@2502: /// It reads an label from the stream and gives back which edge belongs to deba@2502: /// it. It is possible only if there was read a "label" named edge map. deba@2502: void readLabel(std::istream& is, Edge& edge) const { deba@2502: return uedgeset_reader.readLabel(is, edge); deba@2502: } deba@2502: deba@2502: /// \brief Gives back the undirected edge by its label. deba@2502: /// deba@2502: /// It reads an label from the stream and gives back which undirected edge deba@2502: /// belongs to it. It is possible only if there was read a "label" named deba@2502: /// edge map. deba@2502: void readLabel(std::istream& is, UEdge& uedge) const { deba@2502: return uedgeset_reader.readLabel(is, uedge); deba@2502: } deba@2502: deba@2502: deba@2502: private: deba@2502: deba@2502: LemonReader* reader; deba@2502: bool own_reader; deba@2502: deba@2502: DefaultSkipper skipper; deba@2502: deba@2502: BpNodeSetReader nodeset_reader; deba@2502: UEdgeSetReader uedgeset_reader; deba@2502: deba@2502: NodeReader node_reader; deba@2502: UEdgeReader uedge_reader; deba@2502: deba@2502: AttributeReader attribute_reader; deba@2502: }; deba@2502: deba@1421: deba@1333: /// @} deba@1137: } deba@1214: deba@1214: #endif