diff --git a/lemon/lgf_writer.h b/lemon/lgf_writer.h --- a/lemon/lgf_writer.h +++ b/lemon/lgf_writer.h @@ -71,6 +71,27 @@ } }; + template + class GraphArcMapLess { + public: + typedef _Map Map; + typedef _Graph Graph; + typedef typename Graph::Edge Item; + + private: + const Graph& _graph; + const Map& _map; + + public: + GraphArcMapLess(const Graph& graph, const Map& map) + : _graph(graph), _map(map) {} + + bool operator()(const Item& left, const Item& right) { + return _map[_graph.direct(left, _dir)] < + _map[_graph.direct(right, _dir)]; + } + }; + template class MapStorageBase { public: @@ -110,6 +131,36 @@ } }; + template > + class GraphArcMapStorage : public MapStorageBase { + public: + typedef _Map Map; + typedef _Converter Converter; + typedef _Graph Graph; + typedef typename Graph::Edge Item; + static const bool dir = _dir; + + private: + const Graph& _graph; + const Map& _map; + Converter _converter; + + public: + GraphArcMapStorage(const Graph& graph, const Map& map, + const Converter& converter = Converter()) + : _graph(graph), _map(map), _converter(converter) {} + virtual ~GraphArcMapStorage() {} + + virtual std::string get(const Item& item) { + return _converter(_map[_graph.direct(item, dir)]); + } + virtual void sort(std::vector& items) { + GraphArcMapLess less(_graph, _map); + std::sort(items.begin(), items.end(), less); + } + }; + class ValueStorageBase { public: ValueStorageBase() {} @@ -154,6 +205,26 @@ } }; + template + struct GraphArcLookUpConverter { + const Graph& _graph; + const std::map& _map; + + GraphArcLookUpConverter(const Graph& graph, + const std::map& map) + : _graph(graph), _map(map) {} + + std::string operator()(const typename Graph::Arc& val) { + typename std::map + ::const_iterator it = _map.find(val); + if (it == _map.end()) { + throw DataFormatError("Item not found"); + } + return (_graph.direction(val) ? '+' : '-') + it->second; + } + }; + bool isWhiteSpace(char c) { return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r' || c == '\f'; @@ -738,6 +809,517 @@ DigraphWriter tmp(fn, digraph); return tmp; } + + /// \ingroup lemon_io + /// + /// \brief LGF writer for directed graphs + /// + /// This utility writes an \ref lgf-format "LGF" file. + template + class GraphWriter { + public: + + typedef _Graph Graph; + TEMPLATE_GRAPH_TYPEDEFS(Graph); + + private: + + + std::ostream* _os; + bool local_os; + + Graph& _graph; + + std::string _nodes_caption; + std::string _edges_caption; + std::string _attributes_caption; + + typedef std::map NodeIndex; + NodeIndex _node_index; + typedef std::map EdgeIndex; + EdgeIndex _edge_index; + + typedef std::vector* > > NodeMaps; + NodeMaps _node_maps; + + typedef std::vector* > >EdgeMaps; + EdgeMaps _edge_maps; + + typedef std::vector > Attributes; + Attributes _attributes; + + bool _skip_nodes; + bool _skip_edges; + + public: + + /// \brief Constructor + /// + /// Construct a directed graph writer, which writes to the given + /// output stream. + GraphWriter(std::ostream& is, Graph& graph) + : _os(&is), local_os(false), _graph(graph), + _skip_nodes(false), _skip_edges(false) {} + + /// \brief Constructor + /// + /// Construct a directed graph writer, which writes to the given + /// output file. + GraphWriter(const std::string& fn, Graph& graph) + : _os(new std::ofstream(fn.c_str())), local_os(true), _graph(graph), + _skip_nodes(false), _skip_edges(false) {} + + /// \brief Constructor + /// + /// Construct a directed graph writer, which writes to the given + /// output file. + GraphWriter(const char* fn, Graph& graph) + : _os(new std::ofstream(fn)), local_os(true), _graph(graph), + _skip_nodes(false), _skip_edges(false) {} + + /// \brief Copy constructor + /// + /// The copy constructor transfers all data from the other writer, + /// therefore the copied writer will not be usable more. + GraphWriter(GraphWriter& other) + : _os(other._os), local_os(other.local_os), _graph(other._graph), + _skip_nodes(other._skip_nodes), _skip_edges(other._skip_edges) { + + other._os = 0; + other.local_os = false; + + _node_index.swap(other._node_index); + _edge_index.swap(other._edge_index); + + _node_maps.swap(other._node_maps); + _edge_maps.swap(other._edge_maps); + _attributes.swap(other._attributes); + + _nodes_caption = other._nodes_caption; + _edges_caption = other._edges_caption; + _attributes_caption = other._attributes_caption; + } + + /// \brief Destructor + ~GraphWriter() { + for (typename NodeMaps::iterator it = _node_maps.begin(); + it != _node_maps.end(); ++it) { + delete it->second; + } + + for (typename EdgeMaps::iterator it = _edge_maps.begin(); + it != _edge_maps.end(); ++it) { + delete it->second; + } + + for (typename Attributes::iterator it = _attributes.begin(); + it != _attributes.end(); ++it) { + delete it->second; + } + + if (local_os) { + delete _os; + } + } + + private: + + GraphWriter& operator=(const GraphWriter&); + + public: + + /// \name Writing rules + /// @{ + + /// \brief Node map reading rule + /// + /// Add a node map reading rule to the writer. + template + GraphWriter& nodeMap(const std::string& caption, const Map& map) { + checkConcept, Map>(); + _writer_bits::MapStorageBase* storage = + new _writer_bits::MapStorage(map); + _node_maps.push_back(std::make_pair(caption, storage)); + return *this; + } + + /// \brief Node map writing rule + /// + /// Add a node map writing rule with specialized converter to the + /// writer. + template + GraphWriter& nodeMap(const std::string& caption, const Map& map, + const Converter& converter = Converter()) { + checkConcept, Map>(); + _writer_bits::MapStorageBase* storage = + new _writer_bits::MapStorage(map, converter); + _node_maps.push_back(std::make_pair(caption, storage)); + return *this; + } + + /// \brief Edge map writing rule + /// + /// Add an edge map writing rule to the writer. + template + GraphWriter& edgeMap(const std::string& caption, const Map& map) { + checkConcept, Map>(); + _writer_bits::MapStorageBase* storage = + new _writer_bits::MapStorage(map); + _edge_maps.push_back(std::make_pair(caption, storage)); + return *this; + } + + /// \brief Edge map writing rule + /// + /// Add an edge map writing rule with specialized converter to the + /// writer. + template + GraphWriter& edgeMap(const std::string& caption, const Map& map, + const Converter& converter = Converter()) { + checkConcept, Map>(); + _writer_bits::MapStorageBase* storage = + new _writer_bits::MapStorage(map, converter); + _edge_maps.push_back(std::make_pair(caption, storage)); + return *this; + } + + /// \brief Arc map writing rule + /// + /// Add an arc map writing rule to the writer. + template + GraphWriter& arcMap(const std::string& caption, const Map& map) { + checkConcept, Map>(); + _writer_bits::MapStorageBase* forward_storage = + new _writer_bits::GraphArcMapStorage(_graph, map); + _edge_maps.push_back(std::make_pair('+' + caption, forward_storage)); + _writer_bits::MapStorageBase* backward_storage = + new _writer_bits::GraphArcMapStorage(_graph, map); + _edge_maps.push_back(std::make_pair('-' + caption, backward_storage)); + return *this; + } + + /// \brief Arc map writing rule + /// + /// Add an arc map writing rule with specialized converter to the + /// writer. + template + GraphWriter& arcMap(const std::string& caption, const Map& map, + const Converter& converter = Converter()) { + checkConcept, Map>(); + _writer_bits::MapStorageBase* forward_storage = + new _writer_bits::GraphArcMapStorage + (_graph, map, converter); + _edge_maps.push_back(std::make_pair('+' + caption, forward_storage)); + _writer_bits::MapStorageBase* backward_storage = + new _writer_bits::GraphArcMapStorage + (_graph, map, converter); + _edge_maps.push_back(std::make_pair('-' + caption, backward_storage)); + return *this; + } + + /// \brief Attribute writing rule + /// + /// Add an attribute writing rule to the writer. + template + GraphWriter& attribute(const std::string& caption, const Value& value) { + _writer_bits::ValueStorageBase* storage = + new _writer_bits::ValueStorage(value); + _attributes.push_back(std::make_pair(caption, storage)); + return *this; + } + + /// \brief Attribute writing rule + /// + /// Add an attribute writing rule with specialized converter to the + /// writer. + template + GraphWriter& attribute(const std::string& caption, const Value& value, + const Converter& converter = Converter()) { + _writer_bits::ValueStorageBase* storage = + new _writer_bits::ValueStorage(value, converter); + _attributes.push_back(std::make_pair(caption, storage)); + return *this; + } + + /// \brief Node writing rule + /// + /// Add a node writing rule to the writer. + GraphWriter& node(const std::string& caption, const Node& node) { + typedef _writer_bits::MapLookUpConverter Converter; + Converter converter(_node_index); + _writer_bits::ValueStorageBase* storage = + new _writer_bits::ValueStorage(node, converter); + _attributes.push_back(std::make_pair(caption, storage)); + return *this; + } + + /// \brief Edge writing rule + /// + /// Add an edge writing rule to writer. + GraphWriter& edge(const std::string& caption, const Edge& edge) { + typedef _writer_bits::MapLookUpConverter Converter; + Converter converter(_edge_index); + _writer_bits::ValueStorageBase* storage = + new _writer_bits::ValueStorage(edge, converter); + _attributes.push_back(std::make_pair(caption, storage)); + return *this; + } + + /// \brief Arc writing rule + /// + /// Add an arc writing rule to writer. + GraphWriter& arc(const std::string& caption, const Arc& arc) { + typedef _writer_bits::GraphArcLookUpConverter Converter; + Converter converter(_graph, _edge_index); + _writer_bits::ValueStorageBase* storage = + new _writer_bits::ValueStorage(arc, converter); + _attributes.push_back(std::make_pair(caption, storage)); + return *this; + } + + /// \name Select section by name + /// @{ + + /// \brief Set \c \@nodes section to be read + /// + /// Set \c \@nodes section to be read + GraphWriter& nodes(const std::string& caption) { + _nodes_caption = caption; + return *this; + } + + /// \brief Set \c \@edges section to be read + /// + /// Set \c \@edges section to be read + GraphWriter& edges(const std::string& caption) { + _edges_caption = caption; + return *this; + } + + /// \brief Set \c \@attributes section to be read + /// + /// Set \c \@attributes section to be read + GraphWriter& attributes(const std::string& caption) { + _attributes_caption = caption; + return *this; + } + + /// \name Skipping section + /// @{ + + /// \brief Skip writing the node set + /// + /// The \c \@nodes section will be not written to the stream. + GraphWriter& skipNodes() { + LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member"); + return *this; + } + + /// \brief Skip writing edge set + /// + /// The \c \@edges section will be not written to the stream. + GraphWriter& skipEdges() { + LEMON_ASSERT(!_skip_edges, "Multiple usage of skipEdges() member"); + return *this; + } + + /// @} + + private: + + void writeNodes() { + _writer_bits::MapStorageBase* label = 0; + for (typename NodeMaps::iterator it = _node_maps.begin(); + it != _node_maps.end(); ++it) { + if (it->first == "label") { + label = it->second; + break; + } + } + + *_os << "@nodes"; + if (!_nodes_caption.empty()) { + _writer_bits::writeToken(*_os << ' ', _nodes_caption); + } + *_os << std::endl; + + if (label == 0) { + *_os << "label" << '\t'; + } + for (typename NodeMaps::iterator it = _node_maps.begin(); + it != _node_maps.end(); ++it) { + _writer_bits::writeToken(*_os, it->first) << '\t'; + } + *_os << std::endl; + + std::vector nodes; + for (NodeIt n(_graph); n != INVALID; ++n) { + nodes.push_back(n); + } + + if (label == 0) { + IdMap id_map(_graph); + _writer_bits::MapLess > id_less(id_map); + std::sort(nodes.begin(), nodes.end(), id_less); + } else { + label->sort(nodes); + } + + for (int i = 0; i < static_cast(nodes.size()); ++i) { + Node n = nodes[i]; + if (label == 0) { + std::ostringstream os; + os << _graph.id(n); + _writer_bits::writeToken(*_os, os.str()); + *_os << '\t'; + _node_index.insert(std::make_pair(n, os.str())); + } + for (typename NodeMaps::iterator it = _node_maps.begin(); + it != _node_maps.end(); ++it) { + std::string value = it->second->get(n); + _writer_bits::writeToken(*_os, value); + if (it->first == "label") { + _node_index.insert(std::make_pair(n, value)); + } + *_os << '\t'; + } + *_os << std::endl; + } + } + + void writeEdges() { + _writer_bits::MapStorageBase* label = 0; + for (typename EdgeMaps::iterator it = _edge_maps.begin(); + it != _edge_maps.end(); ++it) { + if (it->first == "label") { + label = it->second; + break; + } + } + + *_os << "@edges"; + if (!_edges_caption.empty()) { + _writer_bits::writeToken(*_os << ' ', _edges_caption); + } + *_os << std::endl; + + *_os << '\t' << '\t'; + if (label == 0) { + *_os << "label" << '\t'; + } + for (typename EdgeMaps::iterator it = _edge_maps.begin(); + it != _edge_maps.end(); ++it) { + _writer_bits::writeToken(*_os, it->first) << '\t'; + } + *_os << std::endl; + + std::vector edges; + for (EdgeIt n(_graph); n != INVALID; ++n) { + edges.push_back(n); + } + + if (label == 0) { + IdMap id_map(_graph); + _writer_bits::MapLess > id_less(id_map); + std::sort(edges.begin(), edges.end(), id_less); + } else { + label->sort(edges); + } + + for (int i = 0; i < static_cast(edges.size()); ++i) { + Edge e = edges[i]; + _writer_bits::writeToken(*_os, _node_index. + find(_graph.u(e))->second); + *_os << '\t'; + _writer_bits::writeToken(*_os, _node_index. + find(_graph.v(e))->second); + *_os << '\t'; + if (label == 0) { + std::ostringstream os; + os << _graph.id(e); + _writer_bits::writeToken(*_os, os.str()); + *_os << '\t'; + _edge_index.insert(std::make_pair(e, os.str())); + } + for (typename EdgeMaps::iterator it = _edge_maps.begin(); + it != _edge_maps.end(); ++it) { + std::string value = it->second->get(e); + _writer_bits::writeToken(*_os, value); + if (it->first == "label") { + _edge_index.insert(std::make_pair(e, value)); + } + *_os << '\t'; + } + *_os << std::endl; + } + } + + void writeAttributes() { + if (_attributes.empty()) return; + *_os << "@attributes"; + if (!_attributes_caption.empty()) { + _writer_bits::writeToken(*_os << ' ', _attributes_caption); + } + *_os << std::endl; + for (typename Attributes::iterator it = _attributes.begin(); + it != _attributes.end(); ++it) { + _writer_bits::writeToken(*_os, it->first) << ' '; + _writer_bits::writeToken(*_os, it->second->get()); + *_os << std::endl; + } + } + + public: + + /// \name Execution of the writer + /// @{ + + /// \brief Start the batch processing + /// + /// This function starts the batch processing + void run() { + if (!_skip_nodes) { + writeNodes(); + } + if (!_skip_edges) { + writeEdges(); + } + writeAttributes(); + } + + /// \brief Gives back the stream of the writer + /// + /// Gives back the stream of the writer + std::ostream& ostream() { + return *_os; + } + + /// @} + }; + + /// \relates GraphWriter + template + GraphWriter graphWriter(std::ostream& os, Graph& graph) { + GraphWriter tmp(os, graph); + return tmp; + } + + /// \relates GraphWriter + template + GraphWriter graphWriter(const std::string& fn, Graph& graph) { + GraphWriter tmp(fn, graph); + return tmp; + } + + /// \relates GraphWriter + template + GraphWriter graphWriter(const char* fn, Graph& graph) { + GraphWriter tmp(fn, graph); + return tmp; + } } #endif