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