/* -*- mode: C++; indent-tabs-mode: nil; -*- * * This file is a part of LEMON, a generic C++ optimization library. * * Copyright (C) 2003-2009 * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport * (Egervary Research Group on Combinatorial Optimization, EGRES). * * Permission to use, modify and distribute this software is granted * provided that this copyright notice appears in all copies. For * precise terms see the accompanying LICENSE file. * * This software is provided "AS IS" with no warranty of any kind, * express or implied, and with no claim as to its suitability for any * purpose. * */ ///\ingroup lemon_io ///\file ///\brief \ref lgf-format "LEMON Graph Format" writer. #ifndef LEMON_LGF_WRITER_H #define LEMON_LGF_WRITER_H #include #include #include #include #include #include #include #include #include #include namespace lemon { namespace _writer_bits { template struct DefaultConverter { std::string operator()(const Value& value) { std::ostringstream os; os << value; return os.str(); } }; template bool operator<(const T&, const T&) { throw FormatError("Label map is not comparable"); } template class MapLess { public: typedef _Map Map; typedef typename Map::Key Item; private: const Map& _map; public: MapLess(const Map& map) : _map(map) {} bool operator()(const Item& left, const Item& right) { return _map[left] < _map[right]; } }; 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: typedef _Item Item; public: MapStorageBase() {} virtual ~MapStorageBase() {} virtual std::string get(const Item& item) = 0; virtual void sort(std::vector&) = 0; }; template > class MapStorage : public MapStorageBase<_Item> { public: typedef _Map Map; typedef _Converter Converter; typedef _Item Item; private: const Map& _map; Converter _converter; public: MapStorage(const Map& map, const Converter& converter = Converter()) : _map(map), _converter(converter) {} virtual ~MapStorage() {} virtual std::string get(const Item& item) { return _converter(_map[item]); } virtual void sort(std::vector& items) { MapLess less(_map); std::sort(items.begin(), items.end(), less); } }; 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() {} virtual ~ValueStorageBase() {} virtual std::string get() = 0; }; template > class ValueStorage : public ValueStorageBase { public: typedef _Value Value; typedef _Converter Converter; private: const Value& _value; Converter _converter; public: ValueStorage(const Value& value, const Converter& converter = Converter()) : _value(value), _converter(converter) {} virtual std::string get() { return _converter(_value); } }; template struct MapLookUpConverter { const std::map& _map; MapLookUpConverter(const std::map& map) : _map(map) {} std::string operator()(const Value& str) { typename std::map::const_iterator it = _map.find(str); if (it == _map.end()) { throw FormatError("Item not found"); } return it->second; } }; 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 FormatError("Item not found"); } return (_graph.direction(val) ? '+' : '-') + it->second; } }; inline bool isWhiteSpace(char c) { return c == ' ' || c == '\t' || c == '\v' || c == '\n' || c == '\r' || c == '\f'; } inline bool isEscaped(char c) { return c == '\\' || c == '\"' || c == '\'' || c == '\a' || c == '\b'; } inline static void writeEscape(std::ostream& os, char c) { switch (c) { case '\\': os << "\\\\"; return; case '\"': os << "\\\""; return; case '\a': os << "\\a"; return; case '\b': os << "\\b"; return; case '\f': os << "\\f"; return; case '\r': os << "\\r"; return; case '\n': os << "\\n"; return; case '\t': os << "\\t"; return; case '\v': os << "\\v"; return; default: if (c < 0x20) { std::ios::fmtflags flags = os.flags(); os << '\\' << std::oct << static_cast(c); os.flags(flags); } else { os << c; } return; } } inline bool requireEscape(const std::string& str) { if (str.empty() || str[0] == '@') return true; std::istringstream is(str); char c; while (is.get(c)) { if (isWhiteSpace(c) || isEscaped(c)) { return true; } } return false; } inline std::ostream& writeToken(std::ostream& os, const std::string& str) { if (requireEscape(str)) { os << '\"'; for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { writeEscape(os, *it); } os << '\"'; } else { os << str; } return os; } class Section { public: virtual ~Section() {} virtual void process(std::ostream& os) = 0; }; template class LineSection : public Section { private: Functor _functor; public: LineSection(const Functor& functor) : _functor(functor) {} virtual ~LineSection() {} virtual void process(std::ostream& os) { std::string line; while (!(line = _functor()).empty()) os << line << std::endl; } }; template class StreamSection : public Section { private: Functor _functor; public: StreamSection(const Functor& functor) : _functor(functor) {} virtual ~StreamSection() {} virtual void process(std::ostream& os) { _functor(os); } }; } template class DigraphWriter; template DigraphWriter digraphWriter(const TDGR& digraph, std::ostream& os = std::cout); template DigraphWriter digraphWriter(const TDGR& digraph, const std::string& fn); template DigraphWriter digraphWriter(const TDGR& digraph, const char* fn); /// \ingroup lemon_io /// /// \brief \ref lgf-format "LGF" writer for directed graphs /// /// This utility writes an \ref lgf-format "LGF" file. /// /// The writing method does a batch processing. The user creates a /// writer object, then various writing rules can be added to the /// writer, and eventually the writing is executed with the \c run() /// member function. A map writing rule can be added to the writer /// with the \c nodeMap() or \c arcMap() members. An optional /// converter parameter can also be added as a standard functor /// converting from the value type of the map to \c std::string. If it /// is set, it will determine how the value type of the map is written to /// the output stream. If the functor is not set, then a default /// conversion will be used. The \c attribute(), \c node() and \c /// arc() functions are used to add attribute writing rules. /// ///\code /// DigraphWriter(digraph, std::cout). /// nodeMap("coordinates", coord_map). /// nodeMap("size", size). /// nodeMap("title", title). /// arcMap("capacity", cap_map). /// node("source", src). /// node("target", trg). /// attribute("caption", caption). /// run(); ///\endcode /// /// /// By default, the writer does not write additional captions to the /// sections, but they can be give as an optional parameter of /// the \c nodes(), \c arcs() or \c /// attributes() functions. /// /// The \c skipNodes() and \c skipArcs() functions forbid the /// writing of the sections. If two arc sections should be written /// to the output, it can be done in two passes, the first pass /// writes the node section and the first arc section, then the /// second pass skips the node section and writes just the arc /// section to the stream. The output stream can be retrieved with /// the \c ostream() function, hence the second pass can append its /// output to the output of the first pass. template class DigraphWriter { public: typedef DGR Digraph; TEMPLATE_DIGRAPH_TYPEDEFS(DGR); private: std::ostream* _os; bool local_os; const DGR& _digraph; std::string _nodes_caption; std::string _arcs_caption; std::string _attributes_caption; typedef std::map NodeIndex; NodeIndex _node_index; typedef std::map ArcIndex; ArcIndex _arc_index; typedef std::vector* > > NodeMaps; NodeMaps _node_maps; typedef std::vector* > >ArcMaps; ArcMaps _arc_maps; typedef std::vector > Attributes; Attributes _attributes; bool _skip_nodes; bool _skip_arcs; public: /// \brief Constructor /// /// Construct a directed graph writer, which writes to the given /// output stream. DigraphWriter(const DGR& digraph, std::ostream& os = std::cout) : _os(&os), local_os(false), _digraph(digraph), _skip_nodes(false), _skip_arcs(false) {} /// \brief Constructor /// /// Construct a directed graph writer, which writes to the given /// output file. DigraphWriter(const DGR& digraph, const std::string& fn) : _os(new std::ofstream(fn.c_str())), local_os(true), _digraph(digraph), _skip_nodes(false), _skip_arcs(false) { if (!(*_os)) { delete _os; throw IoError("Cannot write file", fn); } } /// \brief Constructor /// /// Construct a directed graph writer, which writes to the given /// output file. DigraphWriter(const DGR& digraph, const char* fn) : _os(new std::ofstream(fn)), local_os(true), _digraph(digraph), _skip_nodes(false), _skip_arcs(false) { if (!(*_os)) { delete _os; throw IoError("Cannot write file", fn); } } /// \brief Destructor ~DigraphWriter() { for (typename NodeMaps::iterator it = _node_maps.begin(); it != _node_maps.end(); ++it) { delete it->second; } for (typename ArcMaps::iterator it = _arc_maps.begin(); it != _arc_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: template friend DigraphWriter digraphWriter(const TDGR& digraph, std::ostream& os); template friend DigraphWriter digraphWriter(const TDGR& digraph, const std::string& fn); template friend DigraphWriter digraphWriter(const TDGR& digraph, const char *fn); DigraphWriter(DigraphWriter& other) : _os(other._os), local_os(other.local_os), _digraph(other._digraph), _skip_nodes(other._skip_nodes), _skip_arcs(other._skip_arcs) { other._os = 0; other.local_os = false; _node_index.swap(other._node_index); _arc_index.swap(other._arc_index); _node_maps.swap(other._node_maps); _arc_maps.swap(other._arc_maps); _attributes.swap(other._attributes); _nodes_caption = other._nodes_caption; _arcs_caption = other._arcs_caption; _attributes_caption = other._attributes_caption; } DigraphWriter& operator=(const DigraphWriter&); public: /// \name Writing Rules /// @{ /// \brief Node map writing rule /// /// Add a node map writing rule to the writer. template DigraphWriter& 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 DigraphWriter& 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 Arc map writing rule /// /// Add an arc map writing rule to the writer. template DigraphWriter& arcMap(const std::string& caption, const Map& map) { checkConcept, Map>(); _writer_bits::MapStorageBase* storage = new _writer_bits::MapStorage(map); _arc_maps.push_back(std::make_pair(caption, storage)); return *this; } /// \brief Arc map writing rule /// /// Add an arc map writing rule with specialized converter to the /// writer. template DigraphWriter& arcMap(const std::string& caption, const Map& map, const Converter& converter = Converter()) { checkConcept, Map>(); _writer_bits::MapStorageBase* storage = new _writer_bits::MapStorage(map, converter); _arc_maps.push_back(std::make_pair(caption, storage)); return *this; } /// \brief Attribute writing rule /// /// Add an attribute writing rule to the writer. template DigraphWriter& 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 DigraphWriter& 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. DigraphWriter& 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 Arc writing rule /// /// Add an arc writing rule to writer. DigraphWriter& arc(const std::string& caption, const Arc& arc) { typedef _writer_bits::MapLookUpConverter Converter; Converter converter(_arc_index); _writer_bits::ValueStorageBase* storage = new _writer_bits::ValueStorage(arc, converter); _attributes.push_back(std::make_pair(caption, storage)); return *this; } /// \name Section Captions /// @{ /// \brief Add an additional caption to the \c \@nodes section /// /// Add an additional caption to the \c \@nodes section. DigraphWriter& nodes(const std::string& caption) { _nodes_caption = caption; return *this; } /// \brief Add an additional caption to the \c \@arcs section /// /// Add an additional caption to the \c \@arcs section. DigraphWriter& arcs(const std::string& caption) { _arcs_caption = caption; return *this; } /// \brief Add an additional caption to the \c \@attributes section /// /// Add an additional caption to the \c \@attributes section. DigraphWriter& attributes(const std::string& caption) { _attributes_caption = caption; return *this; } /// \name Skipping Section /// @{ /// \brief Skip writing the node set /// /// The \c \@nodes section will not be written to the stream. DigraphWriter& skipNodes() { LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member"); _skip_nodes = true; return *this; } /// \brief Skip writing arc set /// /// The \c \@arcs section will not be written to the stream. DigraphWriter& skipArcs() { LEMON_ASSERT(!_skip_arcs, "Multiple usage of skipArcs() member"); _skip_arcs = true; 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(_digraph); n != INVALID; ++n) { nodes.push_back(n); } if (label == 0) { IdMap id_map(_digraph); _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 << _digraph.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 createNodeIndex() { _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; } } if (label == 0) { for (NodeIt n(_digraph); n != INVALID; ++n) { std::ostringstream os; os << _digraph.id(n); _node_index.insert(std::make_pair(n, os.str())); } } else { for (NodeIt n(_digraph); n != INVALID; ++n) { std::string value = label->get(n); _node_index.insert(std::make_pair(n, value)); } } } void writeArcs() { _writer_bits::MapStorageBase* label = 0; for (typename ArcMaps::iterator it = _arc_maps.begin(); it != _arc_maps.end(); ++it) { if (it->first == "label") { label = it->second; break; } } *_os << "@arcs"; if (!_arcs_caption.empty()) { _writer_bits::writeToken(*_os << ' ', _arcs_caption); } *_os << std::endl; *_os << '\t' << '\t'; if (label == 0) { *_os << "label" << '\t'; } for (typename ArcMaps::iterator it = _arc_maps.begin(); it != _arc_maps.end(); ++it) { _writer_bits::writeToken(*_os, it->first) << '\t'; } *_os << std::endl; std::vector arcs; for (ArcIt n(_digraph); n != INVALID; ++n) { arcs.push_back(n); } if (label == 0) { IdMap id_map(_digraph); _writer_bits::MapLess > id_less(id_map); std::sort(arcs.begin(), arcs.end(), id_less); } else { label->sort(arcs); } for (int i = 0; i < static_cast(arcs.size()); ++i) { Arc a = arcs[i]; _writer_bits::writeToken(*_os, _node_index. find(_digraph.source(a))->second); *_os << '\t'; _writer_bits::writeToken(*_os, _node_index. find(_digraph.target(a))->second); *_os << '\t'; if (label == 0) { std::ostringstream os; os << _digraph.id(a); _writer_bits::writeToken(*_os, os.str()); *_os << '\t'; _arc_index.insert(std::make_pair(a, os.str())); } for (typename ArcMaps::iterator it = _arc_maps.begin(); it != _arc_maps.end(); ++it) { std::string value = it->second->get(a); _writer_bits::writeToken(*_os, value); if (it->first == "label") { _arc_index.insert(std::make_pair(a, value)); } *_os << '\t'; } *_os << std::endl; } } void createArcIndex() { _writer_bits::MapStorageBase* label = 0; for (typename ArcMaps::iterator it = _arc_maps.begin(); it != _arc_maps.end(); ++it) { if (it->first == "label") { label = it->second; break; } } if (label == 0) { for (ArcIt a(_digraph); a != INVALID; ++a) { std::ostringstream os; os << _digraph.id(a); _arc_index.insert(std::make_pair(a, os.str())); } } else { for (ArcIt a(_digraph); a != INVALID; ++a) { std::string value = label->get(a); _arc_index.insert(std::make_pair(a, value)); } } } 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(); } else { createNodeIndex(); } if (!_skip_arcs) { writeArcs(); } else { createArcIndex(); } writeAttributes(); } /// \brief Give back the stream of the writer /// /// Give back the stream of the writer. std::ostream& ostream() { return *_os; } /// @} }; /// \ingroup lemon_io /// /// \brief Return a \ref DigraphWriter class /// /// This function just returns a \ref DigraphWriter class. /// /// With this function a digraph can be write to a file or output /// stream in \ref lgf-format "LGF" format with several maps and /// attributes. For example, with the following code a network flow /// problem can be written to the standard output, i.e. a digraph /// with a \e capacity map on the arcs and \e source and \e target /// nodes: /// ///\code ///ListDigraph digraph; ///ListDigraph::ArcMap cap(digraph); ///ListDigraph::Node src, trg; /// // Setting the capacity map and source and target nodes ///digraphWriter(digraph, std::cout). /// arcMap("capacity", cap). /// node("source", src). /// node("target", trg). /// run(); ///\endcode /// /// For a complete documentation, please see the \ref DigraphWriter /// class documentation. /// \warning Don't forget to put the \ref DigraphWriter::run() "run()" /// to the end of the parameter list. /// \relates DigraphWriter /// \sa digraphWriter(const TDGR& digraph, const std::string& fn) /// \sa digraphWriter(const TDGR& digraph, const char* fn) template DigraphWriter digraphWriter(const TDGR& digraph, std::ostream& os) { DigraphWriter tmp(digraph, os); return tmp; } /// \brief Return a \ref DigraphWriter class /// /// This function just returns a \ref DigraphWriter class. /// \relates DigraphWriter /// \sa digraphWriter(const TDGR& digraph, std::ostream& os) template DigraphWriter digraphWriter(const TDGR& digraph, const std::string& fn) { DigraphWriter tmp(digraph, fn); return tmp; } /// \brief Return a \ref DigraphWriter class /// /// This function just returns a \ref DigraphWriter class. /// \relates DigraphWriter /// \sa digraphWriter(const TDGR& digraph, std::ostream& os) template DigraphWriter digraphWriter(const TDGR& digraph, const char* fn) { DigraphWriter tmp(digraph, fn); return tmp; } template class GraphWriter; template GraphWriter graphWriter(const TGR& graph, std::ostream& os = std::cout); template GraphWriter graphWriter(const TGR& graph, const std::string& fn); template GraphWriter graphWriter(const TGR& graph, const char* fn); /// \ingroup lemon_io /// /// \brief \ref lgf-format "LGF" writer for directed graphs /// /// This utility writes an \ref lgf-format "LGF" file. /// /// It can be used almost the same way as \c DigraphWriter. /// The only difference is that this class can handle edges and /// edge maps as well as arcs and arc maps. /// /// The arc maps are written into the file as two columns, the /// caption of the columns are the name of the map prefixed with \c /// '+' and \c '-'. The arcs are written into the \c \@attributes /// section as a \c '+' or a \c '-' prefix (depends on the direction /// of the arc) and the label of corresponding edge. template class GraphWriter { public: typedef GR Graph; TEMPLATE_GRAPH_TYPEDEFS(GR); private: std::ostream* _os; bool local_os; const GR& _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(const GR& graph, std::ostream& os = std::cout) : _os(&os), 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 GR& graph, const std::string& fn) : _os(new std::ofstream(fn.c_str())), local_os(true), _graph(graph), _skip_nodes(false), _skip_edges(false) { if (!(*_os)) { delete _os; throw IoError("Cannot write file", fn); } } /// \brief Constructor /// /// Construct a directed graph writer, which writes to the given /// output file. GraphWriter(const GR& graph, const char* fn) : _os(new std::ofstream(fn)), local_os(true), _graph(graph), _skip_nodes(false), _skip_edges(false) { if (!(*_os)) { delete _os; throw IoError("Cannot write file", fn); } } /// \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: template friend GraphWriter graphWriter(const TGR& graph, std::ostream& os); template friend GraphWriter graphWriter(const TGR& graph, const std::string& fn); template friend GraphWriter graphWriter(const TGR& graph, const char *fn); 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; } GraphWriter& operator=(const GraphWriter&); public: /// \name Writing Rules /// @{ /// \brief Node map writing rule /// /// Add a node map writing 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 Section Captions /// @{ /// \brief Add an additional caption to the \c \@nodes section /// /// Add an additional caption to the \c \@nodes section. GraphWriter& nodes(const std::string& caption) { _nodes_caption = caption; return *this; } /// \brief Add an additional caption to the \c \@arcs section /// /// Add an additional caption to the \c \@arcs section. GraphWriter& edges(const std::string& caption) { _edges_caption = caption; return *this; } /// \brief Add an additional caption to the \c \@attributes section /// /// Add an additional caption to the \c \@attributes section. 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 not be written to the stream. GraphWriter& skipNodes() { LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member"); _skip_nodes = true; return *this; } /// \brief Skip writing edge set /// /// The \c \@edges section will not be written to the stream. GraphWriter& skipEdges() { LEMON_ASSERT(!_skip_edges, "Multiple usage of skipEdges() member"); _skip_edges = true; 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 createNodeIndex() { _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; } } if (label == 0) { for (NodeIt n(_graph); n != INVALID; ++n) { std::ostringstream os; os << _graph.id(n); _node_index.insert(std::make_pair(n, os.str())); } } else { for (NodeIt n(_graph); n != INVALID; ++n) { std::string value = label->get(n); _node_index.insert(std::make_pair(n, value)); } } } 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 createEdgeIndex() { _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; } } if (label == 0) { for (EdgeIt e(_graph); e != INVALID; ++e) { std::ostringstream os; os << _graph.id(e); _edge_index.insert(std::make_pair(e, os.str())); } } else { for (EdgeIt e(_graph); e != INVALID; ++e) { std::string value = label->get(e); _edge_index.insert(std::make_pair(e, value)); } } } 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(); } else { createNodeIndex(); } if (!_skip_edges) { writeEdges(); } else { createEdgeIndex(); } writeAttributes(); } /// \brief Give back the stream of the writer /// /// Give back the stream of the writer std::ostream& ostream() { return *_os; } /// @} }; /// \ingroup lemon_io /// /// \brief Return a \ref GraphWriter class /// /// This function just returns a \ref GraphWriter class. /// /// With this function a graph can be write to a file or output /// stream in \ref lgf-format "LGF" format with several maps and /// attributes. For example, with the following code a weighted /// matching problem can be written to the standard output, i.e. a /// graph with a \e weight map on the edges: /// ///\code ///ListGraph graph; ///ListGraph::EdgeMap weight(graph); /// // Setting the weight map ///graphWriter(graph, std::cout). /// edgeMap("weight", weight). /// run(); ///\endcode /// /// For a complete documentation, please see the \ref GraphWriter /// class documentation. /// \warning Don't forget to put the \ref GraphWriter::run() "run()" /// to the end of the parameter list. /// \relates GraphWriter /// \sa graphWriter(const TGR& graph, const std::string& fn) /// \sa graphWriter(const TGR& graph, const char* fn) template GraphWriter graphWriter(const TGR& graph, std::ostream& os) { GraphWriter tmp(graph, os); return tmp; } /// \brief Return a \ref GraphWriter class /// /// This function just returns a \ref GraphWriter class. /// \relates GraphWriter /// \sa graphWriter(const TGR& graph, std::ostream& os) template GraphWriter graphWriter(const TGR& graph, const std::string& fn) { GraphWriter tmp(graph, fn); return tmp; } /// \brief Return a \ref GraphWriter class /// /// This function just returns a \ref GraphWriter class. /// \relates GraphWriter /// \sa graphWriter(const TGR& graph, std::ostream& os) template GraphWriter graphWriter(const TGR& graph, const char* fn) { GraphWriter tmp(graph, fn); return tmp; } class SectionWriter; SectionWriter sectionWriter(std::istream& is); SectionWriter sectionWriter(const std::string& fn); SectionWriter sectionWriter(const char* fn); /// \ingroup lemon_io /// /// \brief Section writer class /// /// In the \ref lgf-format "LGF" file extra sections can be placed, /// which contain any data in arbitrary format. Such sections can be /// written with this class. A writing rule can be added to the /// class with two different functions. With the \c sectionLines() /// function a generator can write the section line-by-line, while /// with the \c sectionStream() member the section can be written to /// an output stream. class SectionWriter { private: std::ostream* _os; bool local_os; typedef std::vector > Sections; Sections _sections; public: /// \brief Constructor /// /// Construct a section writer, which writes to the given output /// stream. SectionWriter(std::ostream& os) : _os(&os), local_os(false) {} /// \brief Constructor /// /// Construct a section writer, which writes into the given file. SectionWriter(const std::string& fn) : _os(new std::ofstream(fn.c_str())), local_os(true) { if (!(*_os)) { delete _os; throw IoError("Cannot write file", fn); } } /// \brief Constructor /// /// Construct a section writer, which writes into the given file. SectionWriter(const char* fn) : _os(new std::ofstream(fn)), local_os(true) { if (!(*_os)) { delete _os; throw IoError("Cannot write file", fn); } } /// \brief Destructor ~SectionWriter() { for (Sections::iterator it = _sections.begin(); it != _sections.end(); ++it) { delete it->second; } if (local_os) { delete _os; } } private: friend SectionWriter sectionWriter(std::ostream& os); friend SectionWriter sectionWriter(const std::string& fn); friend SectionWriter sectionWriter(const char* fn); SectionWriter(SectionWriter& other) : _os(other._os), local_os(other.local_os) { other._os = 0; other.local_os = false; _sections.swap(other._sections); } SectionWriter& operator=(const SectionWriter&); public: /// \name Section Writers /// @{ /// \brief Add a section writer with line oriented writing /// /// The first parameter is the type descriptor of the section, the /// second is a generator with std::string values. At the writing /// process, the returned \c std::string will be written into the /// output file until it is an empty string. /// /// For example, an integer vector is written into a section. ///\code /// @numbers /// 12 45 23 78 /// 4 28 38 28 /// 23 6 16 ///\endcode /// /// The generator is implemented as a struct. ///\code /// struct NumberSection { /// std::vector::const_iterator _it, _end; /// NumberSection(const std::vector& data) /// : _it(data.begin()), _end(data.end()) {} /// std::string operator()() { /// int rem_in_line = 4; /// std::ostringstream ls; /// while (rem_in_line > 0 && _it != _end) { /// ls << *(_it++) << ' '; /// --rem_in_line; /// } /// return ls.str(); /// } /// }; /// /// // ... /// /// writer.sectionLines("numbers", NumberSection(vec)); ///\endcode template SectionWriter& sectionLines(const std::string& type, Functor functor) { LEMON_ASSERT(!type.empty(), "Type is empty."); _sections.push_back(std::make_pair(type, new _writer_bits::LineSection(functor))); return *this; } /// \brief Add a section writer with stream oriented writing /// /// The first parameter is the type of the section, the second is /// a functor, which takes a \c std::ostream& parameter. The /// functor writes the section to the output stream. /// \warning The last line must be closed with end-line character. template SectionWriter& sectionStream(const std::string& type, Functor functor) { LEMON_ASSERT(!type.empty(), "Type is empty."); _sections.push_back(std::make_pair(type, new _writer_bits::StreamSection(functor))); return *this; } /// @} public: /// \name Execution of the Writer /// @{ /// \brief Start the batch processing /// /// This function starts the batch processing. void run() { LEMON_ASSERT(_os != 0, "This writer is assigned to an other writer"); for (Sections::iterator it = _sections.begin(); it != _sections.end(); ++it) { (*_os) << '@' << it->first << std::endl; it->second->process(*_os); } } /// \brief Give back the stream of the writer /// /// Returns the stream of the writer std::ostream& ostream() { return *_os; } /// @} }; /// \ingroup lemon_io /// /// \brief Return a \ref SectionWriter class /// /// This function just returns a \ref SectionWriter class. /// /// Please see SectionWriter documentation about the custom section /// output. /// /// \relates SectionWriter /// \sa sectionWriter(const std::string& fn) /// \sa sectionWriter(const char *fn) inline SectionWriter sectionWriter(std::ostream& os) { SectionWriter tmp(os); return tmp; } /// \brief Return a \ref SectionWriter class /// /// This function just returns a \ref SectionWriter class. /// \relates SectionWriter /// \sa sectionWriter(std::ostream& os) inline SectionWriter sectionWriter(const std::string& fn) { SectionWriter tmp(fn); return tmp; } /// \brief Return a \ref SectionWriter class /// /// This function just returns a \ref SectionWriter class. /// \relates SectionWriter /// \sa sectionWriter(std::ostream& os) inline SectionWriter sectionWriter(const char* fn) { SectionWriter tmp(fn); return tmp; } } #endif