deba@1409: /* -*- C++ -*- ladanyi@1435: * lemon/lemon_writer.h - Part of LEMON, a generic C++ optimization library deba@1409: * deba@1409: * Copyright (C) 2005 Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport deba@1409: * (Egervary Research Group on Combinatorial Optimization, EGRES). deba@1409: * deba@1409: * Permission to use, modify and distribute this software is granted deba@1409: * provided that this copyright notice appears in all copies. For deba@1409: * precise terms see the accompanying LICENSE file. deba@1409: * deba@1409: * This software is provided "AS IS" with no warranty of any kind, deba@1409: * express or implied, and with no claim as to its suitability for any deba@1409: * purpose. deba@1409: * deba@1409: */ deba@1409: deba@1409: ///\ingroup io_group deba@1409: ///\file deba@1409: ///\brief Lemon Format writer. deba@1409: deba@1409: #ifndef LEMON_LEMON_WRITER_H deba@1409: #define LEMON_LEMON_WRITER_H deba@1409: deba@1409: #include deba@1409: #include deba@1409: #include deba@1409: #include deba@1409: #include deba@1409: #include deba@1409: #include deba@1409: deba@1409: #include deba@1409: #include deba@1421: #include deba@1409: #include deba@1421: #include deba@1421: #include deba@1705: #include deba@1409: deba@1476: #include deba@1476: #include deba@1476: deba@1409: deba@1409: namespace lemon { deba@1409: deba@1476: namespace _writer_bits { deba@1476: deba@1476: template deba@1476: class ItemIdWriter { deba@1476: public: deba@1476: deba@1476: bool isIdWriter() { return true; } deba@1476: deba@1476: void writeId(std::ostream&, const Item&) {} deba@1476: deba@1476: template deba@1476: struct Constraints { deba@1476: void constraints() { deba@1476: bool b = writer.isIdWriter(); deba@1476: ignore_unused_variable_warning(b); deba@1476: writer.writeId(os, item); deba@1476: } deba@1476: _ItemIdWriter& writer; deba@1476: std::ostream& os; deba@1492: const Item& item; deba@1492: }; deba@1492: deba@1492: }; deba@1492: deba@1492: template deba@1492: class ItemWriter { deba@1492: public: deba@1492: deba@1492: void write(std::ostream&, const Item&) {} deba@1492: deba@1492: template deba@1492: struct Constraints { deba@1492: void constraints() { deba@1492: writer.write(os, item); deba@1492: } deba@1492: _ItemWriter& writer; deba@1492: std::ostream& os; deba@1492: const Item& item; deba@1476: }; deba@1476: deba@1476: }; deba@1476: deba@1705: template deba@1705: struct Ref { typedef const Map& Type; }; deba@1705: deba@1705: template deba@1705: class ForwardComposeMap { deba@1705: public: deba@1705: typedef typename Graph::UndirEdge Key; deba@1705: typedef typename Map::Value Value; deba@1705: deba@1705: ForwardComposeMap(const Graph& _graph, const Map& _map) deba@1705: : graph(_graph), map(_map) {} deba@1705: deba@1705: Value operator[](const Key& key) { deba@1705: return map[graph.direct(key, false)]; deba@1705: } deba@1705: deba@1705: private: deba@1705: typename Ref::Type map; deba@1705: const Graph& graph; deba@1705: }; deba@1705: deba@1705: template deba@1705: ForwardComposeMap deba@1705: forwardComposeMap(const Graph& graph, const Map& map) { deba@1705: return ForwardComposeMap(graph, map); deba@1705: } deba@1705: deba@1705: template deba@1705: class BackwardComposeMap { deba@1705: public: deba@1705: typedef typename Graph::UndirEdge Key; deba@1705: typedef typename Map::Value Value; deba@1705: deba@1705: BackwardComposeMap(const Graph& _graph, const Map& _map) deba@1705: : graph(_graph), map(_map) {} deba@1705: deba@1705: Value operator[](const Key& key) { deba@1705: return map[graph.direct(key, false)]; deba@1705: } deba@1705: deba@1705: private: deba@1705: typename Ref::Type map; deba@1705: const Graph& graph; deba@1705: }; deba@1705: deba@1705: template deba@1705: BackwardComposeMap deba@1705: backwardComposeMap(const Graph& graph, const Map& map) { deba@1705: return BackwardComposeMap(graph, map); deba@1705: } deba@1705: deba@1705: template deba@1705: struct Ref > { deba@1705: typedef ForwardComposeMap Type; deba@1705: }; deba@1705: deba@1705: template deba@1705: struct Ref > { deba@1705: typedef BackwardComposeMap Type; deba@1705: }; deba@1705: deba@1705: template deba@1705: struct Ref > { deba@1705: typedef XMap Type; deba@1705: }; deba@1705: template deba@1705: struct Ref > { deba@1705: typedef ConstXMap Type; deba@1705: }; deba@1705: deba@1705: template deba@1705: struct Ref > { deba@1705: typedef YMap Type; deba@1705: }; deba@1705: template deba@1705: struct Ref > { deba@1705: typedef ConstYMap Type; deba@1705: }; deba@1705: deba@1476: } deba@1476: deba@1409: /// \ingroup io_group deba@1409: /// \brief Lemon Format writer class. deba@1409: /// deba@1409: /// The Lemon Format contains several sections. We do not want to deba@1409: /// determine what sections are in a lemon file we give only a framework deba@1409: /// to write a section oriented format. deba@1409: /// deba@1409: /// In the Lemon Format each section starts with a line contains a \c \@ deba@1409: /// character on the first not white space position. This line is the deba@1409: /// header line of the section. Each next lines belong to this section deba@1409: /// while it does not starts with \c \@ character. This line can start a deba@1409: /// new section or if it can close the file with the \c \@end line. deba@1409: /// The file format ignore the empty lines and it may contain comments deba@1409: /// started with a \c # character to the end of the line. deba@1409: /// deba@1409: /// The framework provides an abstract LemonWriter::SectionWriter class deba@1409: /// what defines the interface of a SectionWriter. The SectionWriter deba@1409: /// has the \c header() member function what gives back the header of the deba@1409: /// section. After that it will be called the \c write() member which deba@1409: /// should write the content of the section. deba@1409: /// deba@1409: /// \relates GraphWriter deba@1409: /// \relates NodeSetWriter deba@1409: /// \relates EdgeSetWriter deba@1409: /// \relates NodesWriter deba@1409: /// \relates EdgesWriter deba@1409: /// \relates AttributeWriter deba@1409: class LemonWriter { deba@1409: public: deba@1409: deba@1409: /// \brief Abstract base class for writing a section. deba@1409: /// deba@1409: /// This class has an \c header() member function what gives back deba@1409: /// the header line of the section. The \c write() member should deba@1409: /// write the content of the section to the stream. deba@1409: class SectionWriter { deba@1409: friend class LemonWriter; deba@1409: protected: deba@1409: /// \brief Constructor for SectionWriter. deba@1409: /// deba@1409: /// Constructor for SectionWriter. It attach this writer to deba@1409: /// the given LemonWriter. deba@1409: SectionWriter(LemonWriter& writer) { deba@1409: writer.attach(*this); deba@1409: } alpar@1494: alpar@1494: virtual ~SectionWriter() {} deba@1409: deba@1409: /// \brief The header of section. deba@1409: /// deba@1409: /// It gives back the header of the section. deba@1409: virtual std::string header() = 0; deba@1409: deba@1409: /// \brief Writer function of the section. deba@1409: /// deba@1409: /// Write the content of the section. deba@1409: virtual void write(std::ostream& os) = 0; deba@1409: }; deba@1409: deba@1409: /// \brief Constructor for LemonWriter. deba@1409: /// deba@1409: /// Constructor for LemonWriter which writes to the given stream. deba@1409: LemonWriter(std::ostream& _os) deba@1409: : os(&_os), own_os(false) {} deba@1409: deba@1409: /// \brief Constructor for LemonWriter. deba@1409: /// deba@1409: /// Constructor for LemonWriter which writes to the given file. deba@1409: LemonWriter(const std::string& filename) deba@1409: : os(0), own_os(true) { deba@1409: os = new std::ofstream(filename.c_str()); deba@1409: } deba@1409: deba@1409: /// \brief Desctructor for LemonWriter. deba@1409: /// deba@1409: /// Desctructor for LemonWriter. deba@1409: ~LemonWriter() { deba@1409: if (own_os) { deba@1409: delete os; deba@1409: } deba@1409: } deba@1409: deba@1409: private: deba@1409: LemonWriter(const LemonWriter&); deba@1409: void operator=(const LemonWriter&); deba@1409: deba@1409: void attach(SectionWriter& writer) { deba@1409: writers.push_back(&writer); deba@1409: } deba@1409: deba@1409: public: deba@1409: deba@1409: /// \brief Executes the LemonWriter. deba@1409: /// deba@1409: /// It executes the LemonWriter. deba@1409: void run() { deba@1409: SectionWriters::iterator it; deba@1409: for (it = writers.begin(); it != writers.end(); ++it) { deba@1409: *os << (*it)->header() << std::endl; deba@1409: (*it)->write(*os); deba@1409: } deba@1409: *os << "@end" << std::endl; deba@1409: } deba@1409: deba@1409: deba@1409: private: deba@1409: deba@1409: std::ostream* os; deba@1409: bool own_os; deba@1409: deba@1409: typedef std::vector SectionWriters; deba@1409: SectionWriters writers; deba@1409: deba@1409: }; deba@1409: deba@1409: /// \brief Helper class for implementing the common SectionWriters. deba@1409: /// deba@1409: /// Helper class for implementing the common SectionWriters. deba@1409: class CommonSectionWriterBase : public LemonWriter::SectionWriter { deba@1409: typedef LemonWriter::SectionWriter Parent; deba@1409: protected: deba@1409: deba@1409: /// \brief Constructor for CommonSectionWriterBase. deba@1409: /// deba@1409: /// Constructor for CommonSectionWriterBase. It attach this writer to deba@1409: /// the given LemonWriter. deba@1409: CommonSectionWriterBase(LemonWriter& _writer) deba@1409: : Parent(_writer) {} deba@1409: deba@1409: template deba@1409: class WriterBase { deba@1409: public: deba@1409: typedef _Item Item; deba@1409: deba@1409: virtual ~WriterBase() {} deba@1409: deba@1409: virtual void write(std::ostream& os, const Item& item) = 0; deba@1409: }; deba@1409: deba@1409: deba@1409: template deba@1409: class MapWriter : public WriterBase<_Item> { deba@1409: public: deba@1409: typedef _Map Map; deba@1409: typedef _Writer Writer; deba@1409: typedef typename Writer::Value Value; deba@1409: typedef _Item Item; deba@1409: deba@1705: typename _writer_bits::Ref::Type map; deba@1409: Writer writer; deba@1409: deba@1409: MapWriter(const Map& _map, const Writer& _writer) deba@1409: : map(_map), writer(_writer) {} deba@1409: deba@1409: virtual ~MapWriter() {} deba@1409: deba@1409: virtual void write(std::ostream& os, const Item& item) { deba@1409: Value value = map[item]; deba@1409: writer.write(os, value); deba@1409: } deba@1409: deba@1409: }; deba@1409: deba@1409: deba@1409: class ValueWriterBase { deba@1409: public: alpar@1494: virtual ~ValueWriterBase() {} deba@1409: virtual void write(std::ostream&) = 0; deba@1409: }; deba@1409: deba@1409: template deba@1409: class ValueWriter : public ValueWriterBase { deba@1409: public: deba@1409: typedef _Value Value; deba@1409: typedef _Writer Writer; deba@1409: deba@1409: ValueWriter(const Value& _value, const Writer& _writer) deba@1409: : value(_value), writer(_writer) {} deba@1409: deba@1409: virtual void write(std::ostream& os) { deba@1411: writer.write(os, value); deba@1409: } deba@1409: private: deba@1409: const Value& value; deba@1409: Writer writer; deba@1409: }; deba@1409: deba@1409: deba@1409: template deba@1409: class IdWriterBase { deba@1409: public: deba@1409: typedef _Item Item; alpar@1494: virtual ~IdWriterBase() {} deba@1409: virtual void write(std::ostream&, const Item&) const = 0; deba@1476: virtual bool isIdWriter() const = 0; deba@1409: }; deba@1409: deba@1409: template deba@1409: class IdWriter : public IdWriterBase<_Item> { deba@1409: public: deba@1409: typedef _Item Item; deba@1409: typedef _BoxedIdWriter BoxedIdWriter; deba@1409: deba@1409: const BoxedIdWriter& idWriter; deba@1409: deba@1409: IdWriter(const BoxedIdWriter& _idWriter) deba@1409: : idWriter(_idWriter) {} deba@1409: deba@1409: virtual void write(std::ostream& os, const Item& item) const { deba@1429: idWriter.writeId(os, item); deba@1409: } deba@1476: deba@1476: virtual bool isIdWriter() const { deba@1476: return idWriter.isIdWriter(); deba@1476: } deba@1409: }; deba@1409: }; deba@1409: deba@1409: /// \ingroup io_group deba@1409: /// \brief SectionWriter for writing a graph's nodeset. deba@1409: /// deba@1409: /// The lemon format can store multiple graph nodesets with several maps. deba@1409: /// The nodeset section's header line is \c \@nodeset \c nodeset_id, but the deba@1409: /// \c nodeset_id may be empty. deba@1409: /// deba@1409: /// The first line of the section contains the names of the maps separated deba@1409: /// with white spaces. Each next lines describes a node in the nodeset, and deba@1409: /// contains the mapped values for each map. deba@1409: /// deba@1409: /// If the nodeset contains an \c "id" named map then it will be regarded deba@1409: /// as id map. This map should contain only unique values and when the deba@1409: /// \c writeId() member will be called with a node it will write it's id. deba@1409: /// Otherwise if the \c _forceIdMap constructor parameter is true then deba@1409: /// the id map will be the id in the graph. deba@1409: /// deba@1409: /// \relates LemonWriter deba@1409: template deba@1409: class NodeSetWriter : public CommonSectionWriterBase { deba@1409: typedef CommonSectionWriterBase Parent; deba@1409: public: deba@1409: deba@1409: typedef _Graph Graph; deba@1409: typedef _Traits Traits; deba@1429: typedef typename Graph::Node Node; deba@1409: deba@1409: /// \brief Constructor. deba@1409: /// deba@1409: /// Constructor for NodeSetWriter. It creates the NodeSetWriter and deba@1409: /// attach it into the given LemonWriter. If the \c _forceIdMap deba@1409: /// parameter is true then the writer will write own id map when deba@1409: /// the user does not give "id" named map. deba@1409: NodeSetWriter(LemonWriter& _writer, const Graph& _graph, deba@1409: const std::string& _id = std::string(), deba@1409: bool _forceIdMap = true) deba@1409: : Parent(_writer), idMap(0), forceIdMap(_forceIdMap), deba@1409: graph(_graph), id(_id) {} deba@1409: deba@1409: /// \brief Destructor. deba@1409: /// deba@1409: /// Destructor for NodeSetWriter. deba@1409: virtual ~NodeSetWriter() { deba@1409: typename MapWriters::iterator it; deba@1409: for (it = writers.begin(); it != writers.end(); ++it) { deba@1409: delete it->second; deba@1409: } deba@1409: } deba@1409: deba@1409: private: deba@1409: NodeSetWriter(const NodeSetWriter&); deba@1409: void operator=(const NodeSetWriter&); deba@1409: deba@1409: public: deba@1409: deba@1409: /// \brief Add a new node map writer command for the writer. deba@1409: /// deba@1409: /// Add a new node map writer command for the writer. deba@1409: template deba@1421: NodeSetWriter& writeNodeMap(std::string name, const Map& map) { deba@1421: return writeNodeMap, Map>(name, map); deba@1409: } deba@1409: deba@1409: /// \brief Add a new node map writer command for the writer. deba@1409: /// deba@1409: /// Add a new node map writer command for the writer. deba@1409: template deba@1421: NodeSetWriter& writeNodeMap(std::string name, const Map& map, deba@1421: const Writer& writer = Writer()) { deba@1492: checkConcept, Map>(); deba@1492: checkConcept<_writer_bits::ItemWriter, Writer>(); deba@1409: writers.push_back( deba@1429: make_pair(name, new MapWriter(map, writer))); deba@1409: return *this; deba@1409: } deba@1409: deba@1409: protected: deba@1409: deba@1409: /// \brief The header of the section. deba@1409: /// deba@1409: /// It gives back the header of the section. deba@1409: virtual std::string header() { deba@1409: return "@nodeset " + id; deba@1409: } deba@1409: deba@1409: /// \brief Writer function of the section. deba@1409: /// deba@1409: /// Write the content of the section. deba@1409: virtual void write(std::ostream& os) { deba@1409: for (int i = 0; i < (int)writers.size(); ++i) { deba@1409: if (writers[i].first == "id") { deba@1409: idMap = writers[i].second; deba@1409: forceIdMap = false; deba@1409: break; deba@1409: } deba@1409: } deba@1409: if (forceIdMap) { deba@1409: os << "id\t"; deba@1409: } deba@1409: for (int i = 0; i < (int)writers.size(); ++i) { deba@1409: os << writers[i].first << '\t'; deba@1409: } deba@1409: os << std::endl; deba@1409: for (typename Graph::NodeIt it(graph); it != INVALID; ++it) { deba@1409: if (forceIdMap) { deba@1409: os << graph.id(it) << '\t'; deba@1409: } deba@1409: for (int i = 0; i < (int)writers.size(); ++i) { deba@1409: writers[i].second->write(os, it); deba@1409: os << '\t'; deba@1409: } deba@1409: os << std::endl; deba@1409: } deba@1409: } deba@1409: deba@1409: public: deba@1409: deba@1409: /// \brief Returns true if the nodeset can write the ids of the nodes. deba@1409: /// deba@1409: /// Returns true if the nodeset can write the ids of the nodes. deba@1409: /// It is possible only if an "id" named map was written or the deba@1409: /// \c _forceIdMap constructor parameter was true. deba@1409: bool isIdWriter() const { deba@1409: return idMap != 0 || forceIdMap; deba@1409: } deba@1409: deba@1409: /// \brief Write the id of the given node. deba@1409: /// deba@1409: /// It writes the id of the given node. If there was written an "id" deba@1409: /// named map then it will write the map value belongs to the node. deba@1409: /// Otherwise if the \c forceId parameter was true it will write deba@1409: /// its id in the graph. deba@1429: void writeId(std::ostream& os, const Node& item) const { deba@1409: if (forceIdMap) { deba@1409: os << graph.id(item); deba@1409: } else { deba@1409: idMap->write(os, item); deba@1409: } deba@1409: } deba@1409: deba@1409: private: deba@1409: deba@1429: typedef std::vector*> > MapWriters; deba@1409: MapWriters writers; deba@1409: deba@1429: WriterBase* idMap; deba@1409: bool forceIdMap; deba@1409: deba@1705: const Graph& graph; deba@1409: std::string id; deba@1409: deba@1409: }; deba@1409: deba@1409: /// \ingroup io_group deba@1421: /// \brief SectionWriter for writing a graph's edgesets. deba@1409: /// deba@1421: /// The lemon format can store multiple graph edgesets with several maps. deba@1409: /// The edgeset section's header line is \c \@edgeset \c edgeset_id, but the deba@1409: /// \c edgeset_id may be empty. deba@1409: /// deba@1409: /// The first line of the section contains the names of the maps separated deba@1409: /// with white spaces. Each next lines describes a edge in the edgeset. The deba@1409: /// line contains the source and the target nodes' id and the mapped deba@1409: /// values for each map. deba@1409: /// deba@1409: /// If the edgeset contains an \c "id" named map then it will be regarded deba@1409: /// as id map. This map should contain only unique values and when the deba@1421: /// \c writeId() member will be called with an edge it will write it's id. deba@1409: /// Otherwise if the \c _forceIdMap constructor parameter is true then deba@1409: /// the id map will be the id in the graph. deba@1409: /// deba@1409: /// The edgeset writer needs a node id writer to identify which nodes deba@1409: /// have to be connected. If a NodeSetWriter can write the nodes' id, deba@1409: /// it will be able to use with this class. deba@1409: /// deba@1409: /// \relates LemonWriter deba@1409: template deba@1409: class EdgeSetWriter : public CommonSectionWriterBase { deba@1409: typedef CommonSectionWriterBase Parent; deba@1409: public: deba@1409: deba@1409: typedef _Graph Graph; deba@1409: typedef _Traits Traits; deba@1429: typedef typename Graph::Node Node; deba@1429: typedef typename Graph::Edge Edge; deba@1409: deba@1409: /// \brief Constructor. deba@1409: /// deba@1409: /// Constructor for EdgeSetWriter. It creates the EdgeSetWriter and deba@1409: /// attach it into the given LemonWriter. It will write node ids by deba@1409: /// the \c _nodeIdWriter. If the \c _forceIdMap parameter is true deba@1421: /// then the writer will write own id map if the user does not give deba@1409: /// "id" named map. deba@1409: template deba@1409: EdgeSetWriter(LemonWriter& _writer, const Graph& _graph, deba@1409: const NodeIdWriter& _nodeIdWriter, deba@1409: const std::string& _id = std::string(), deba@1409: bool _forceIdMap = true) deba@1409: : Parent(_writer), idMap(0), forceIdMap(_forceIdMap), deba@1476: graph(_graph), id(_id) { deba@1476: checkConcept<_writer_bits::ItemIdWriter, NodeIdWriter>(); deba@1476: nodeIdWriter.reset(new IdWriter(_nodeIdWriter)); deba@1476: } deba@1409: deba@1409: /// \brief Destructor. deba@1409: /// deba@1409: /// Destructor for EdgeSetWriter. deba@1409: virtual ~EdgeSetWriter() { deba@1409: typename MapWriters::iterator it; deba@1409: for (it = writers.begin(); it != writers.end(); ++it) { deba@1409: delete it->second; deba@1409: } deba@1409: } deba@1409: deba@1409: private: deba@1409: EdgeSetWriter(const EdgeSetWriter&); deba@1409: void operator=(const EdgeSetWriter&); deba@1409: deba@1409: public: deba@1409: deba@1421: /// \brief Add a new edge map writer command for the writer. deba@1409: /// deba@1421: /// Add a new edge map writer command for the writer. deba@1409: template deba@1421: EdgeSetWriter& writeEdgeMap(std::string name, const Map& map) { deba@1421: return writeEdgeMap, Map>(name, map); deba@1409: } deba@1409: deba@1421: /// \brief Add a new edge map writer command for the writer. deba@1409: /// deba@1421: /// Add a new edge map writer command for the writer. deba@1409: template deba@1421: EdgeSetWriter& writeEdgeMap(std::string name, const Map& map, deba@1421: const Writer& writer = Writer()) { deba@1492: checkConcept, Map>(); deba@1492: checkConcept<_writer_bits::ItemWriter, Writer>(); deba@1409: writers.push_back( deba@1429: make_pair(name, new MapWriter(map, writer))); deba@1409: return *this; deba@1409: } deba@1409: deba@1409: protected: deba@1409: deba@1409: /// \brief The header of the section. deba@1409: /// deba@1409: /// It gives back the header of the section. deba@1409: virtual std::string header() { deba@1409: return "@edgeset " + id; deba@1409: } deba@1409: deba@1409: /// \brief Writer function of the section. deba@1409: /// deba@1409: /// Write the content of the section. deba@1409: virtual void write(std::ostream& os) { deba@1476: if (!nodeIdWriter->isIdWriter()) { deba@1476: throw DataFormatError("Cannot find nodeset or ID map"); deba@1476: } deba@1409: for (int i = 0; i < (int)writers.size(); ++i) { deba@1409: if (writers[i].first == "id") { deba@1409: idMap = writers[i].second; deba@1409: forceIdMap = false; deba@1409: break; deba@1409: } deba@1409: } deba@1409: os << "\t\t"; deba@1409: if (forceIdMap) { deba@1409: os << "id\t"; deba@1409: } deba@1409: for (int i = 0; i < (int)writers.size(); ++i) { deba@1409: os << writers[i].first << '\t'; deba@1409: } deba@1409: os << std::endl; deba@1409: for (typename Graph::EdgeIt it(graph); it != INVALID; ++it) { deba@1409: nodeIdWriter->write(os, graph.source(it)); deba@1409: os << '\t'; deba@1409: nodeIdWriter->write(os, graph.target(it)); deba@1409: os << '\t'; deba@1409: if (forceIdMap) { deba@1409: os << graph.id(it) << '\t'; deba@1409: } deba@1409: for (int i = 0; i < (int)writers.size(); ++i) { deba@1409: writers[i].second->write(os, it); deba@1409: os << '\t'; deba@1409: } deba@1409: os << std::endl; deba@1409: } deba@1409: } deba@1409: deba@1409: public: deba@1409: deba@1409: /// \brief Returns true if the edgeset can write the ids of the edges. deba@1409: /// deba@1409: /// Returns true if the edgeset can write the ids of the edges. deba@1409: /// It is possible only if an "id" named map was written or the deba@1409: /// \c _forceIdMap constructor parameter was true. deba@1409: bool isIdWriter() const { deba@1409: return forceIdMap || idMap != 0; deba@1409: } deba@1409: deba@1409: /// \brief Write the id of the given edge. deba@1409: /// deba@1409: /// It writes the id of the given edge. If there was written an "id" deba@1409: /// named map then it will write the map value belongs to the edge. deba@1409: /// Otherwise if the \c forceId parameter was true it will write deba@1409: /// its id in the graph. deba@1429: void writeId(std::ostream& os, const Edge& item) const { deba@1409: if (forceIdMap) { deba@1409: os << graph.id(item); deba@1409: } else { deba@1409: idMap->write(os, item); deba@1409: } deba@1409: } deba@1409: deba@1409: private: deba@1409: deba@1429: typedef std::vector*> > MapWriters; deba@1409: MapWriters writers; deba@1409: deba@1429: WriterBase* idMap; deba@1409: bool forceIdMap; deba@1409: deba@1705: const Graph& graph; deba@1421: std::string id; deba@1421: deba@1429: std::auto_ptr > nodeIdWriter; deba@1421: }; deba@1421: deba@1421: /// \ingroup io_group deba@1421: /// \brief SectionWriter for writing a undirected edgeset. deba@1421: /// deba@1421: /// The lemon format can store multiple undirected edgesets with several deba@1421: /// maps. The undirected edgeset section's header line is \c \@undiredgeset deba@1421: /// \c undiredgeset_id, but the \c undiredgeset_id may be empty. deba@1421: /// deba@1421: /// The first line of the section contains the names of the maps separated deba@1421: /// with white spaces. Each next lines describes an undirected edge in the deba@1421: /// edgeset. The line contains the two connected nodes' id and the mapped deba@1421: /// values for each undirected map. deba@1421: /// deba@1421: /// The section can handle the directed as a syntactical sugar. Two deba@1421: /// undirected edge map describes one directed edge map. This two maps deba@1421: /// are the forward map and the backward map and the names of this map deba@1421: /// is near the same just with a prefix \c '+' or \c '-' character deba@1421: /// difference. deba@1421: /// deba@1421: /// If the edgeset contains an \c "id" named map then it will be regarded deba@1421: /// as id map. This map should contain only unique values and when the deba@1421: /// \c writeId() member will be called with an undirected edge it will deba@1421: /// write it's id. Otherwise if the \c _forceIdMap constructor parameter deba@1421: /// is true then the id map will be the id in the graph. deba@1421: /// deba@1421: /// The undirected edgeset writer needs a node id writer to identify deba@1421: /// which nodes have to be connected. If a NodeSetWriter can write the deba@1421: /// nodes' id, it will be able to use with this class. deba@1421: /// deba@1421: /// \relates LemonWriter deba@1421: template deba@1421: class UndirEdgeSetWriter : public CommonSectionWriterBase { deba@1421: typedef CommonSectionWriterBase Parent; deba@1421: public: deba@1421: deba@1421: typedef _Graph Graph; deba@1421: typedef _Traits Traits; deba@1429: typedef typename Graph::Node Node; deba@1429: typedef typename Graph::Edge Edge; deba@1429: typedef typename Graph::UndirEdge UndirEdge; deba@1421: deba@1421: /// \brief Constructor. deba@1421: /// deba@1421: /// Constructor for UndirEdgeSetWriter. It creates the UndirEdgeSetWriter deba@1421: /// and attach it into the given LemonWriter. It will write node ids by deba@1421: /// the \c _nodeIdWriter. If the \c _forceIdMap parameter is true deba@1421: /// then the writer will write own id map if the user does not give deba@1421: /// "id" named map. deba@1421: template deba@1421: UndirEdgeSetWriter(LemonWriter& _writer, const Graph& _graph, deba@1421: const NodeIdWriter& _nodeIdWriter, deba@1421: const std::string& _id = std::string(), deba@1421: bool _forceIdMap = true) deba@1421: : Parent(_writer), idMap(0), forceIdMap(_forceIdMap), deba@1476: graph(_graph), id(_id) { deba@1476: checkConcept<_writer_bits::ItemIdWriter, NodeIdWriter>(); deba@1476: nodeIdWriter.reset(new IdWriter(_nodeIdWriter)); deba@1476: } deba@1421: deba@1421: /// \brief Destructor. deba@1421: /// deba@1421: /// Destructor for UndirEdgeSetWriter. deba@1421: virtual ~UndirEdgeSetWriter() { deba@1421: typename MapWriters::iterator it; deba@1421: for (it = writers.begin(); it != writers.end(); ++it) { deba@1421: delete it->second; deba@1421: } deba@1421: } deba@1421: deba@1421: private: deba@1421: UndirEdgeSetWriter(const UndirEdgeSetWriter&); deba@1421: void operator=(const UndirEdgeSetWriter&); deba@1421: deba@1421: public: deba@1421: deba@1421: /// \brief Add a new undirected edge map writer command for the writer. deba@1421: /// deba@1421: /// Add a new undirected map writer command for the writer. deba@1421: template deba@1421: UndirEdgeSetWriter& writeUndirEdgeMap(std::string name, const Map& map) { deba@1421: return writeUndirEdgeMap, Map>(name, map); deba@1421: } deba@1421: deba@1421: /// \brief Add a new undirected map writer command for the writer. deba@1421: /// deba@1421: /// Add a new undirected map writer command for the writer. deba@1421: template deba@1421: UndirEdgeSetWriter& writeUndirEdgeMap(std::string name, const Map& map, deba@1421: const Writer& writer = Writer()) { deba@1492: checkConcept, Map>(); deba@1492: checkConcept<_writer_bits::ItemWriter, Writer>(); deba@1421: writers.push_back( deba@1429: make_pair(name, new MapWriter(map, writer))); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: /// \brief Add a new directed edge map writer command for the writer. deba@1421: /// deba@1421: /// Add a new directed map writer command for the writer. deba@1421: template deba@1421: UndirEdgeSetWriter& writeEdgeMap(std::string name, const Map& map) { deba@1492: return writeEdgeMap, Map>(name, map); deba@1421: } deba@1421: deba@1421: /// \brief Add a new directed map writer command for the writer. deba@1421: /// deba@1421: /// Add a new directed map writer command for the writer. deba@1421: template deba@1421: UndirEdgeSetWriter& writeEdgeMap(std::string name, const Map& map, deba@1421: const Writer& writer = Writer()) { deba@1492: checkConcept, Map>(); deba@1492: checkConcept<_writer_bits::ItemWriter, Writer>(); deba@1705: writeUndirEdge("+" + name, deba@1705: _writer_bits::forwardComposeMap(graph, map), writer); deba@1705: writeUndirEdge("-" + name, deba@1705: _writer_bits::backwardComposeMap(graph, map), writer); deba@1421: return *this; deba@1421: } deba@1421: deba@1421: protected: deba@1421: deba@1421: /// \brief The header of the section. deba@1421: /// deba@1421: /// It gives back the header of the section. deba@1421: virtual std::string header() { deba@1421: return "@undiredgeset " + id; deba@1421: } deba@1421: deba@1421: /// \brief Writer function of the section. deba@1421: /// deba@1421: /// Write the content of the section. deba@1421: virtual void write(std::ostream& os) { deba@1476: if (!nodeIdWriter->isIdWriter()) { deba@1476: throw DataFormatError("Cannot find nodeset or ID map"); deba@1476: } deba@1421: for (int i = 0; i < (int)writers.size(); ++i) { deba@1421: if (writers[i].first == "id") { deba@1421: idMap = writers[i].second; deba@1421: forceIdMap = false; deba@1421: break; deba@1421: } deba@1421: } deba@1421: os << "\t\t"; deba@1421: if (forceIdMap) { deba@1421: os << "id\t"; deba@1421: } deba@1421: for (int i = 0; i < (int)writers.size(); ++i) { deba@1421: os << writers[i].first << '\t'; deba@1421: } deba@1421: os << std::endl; deba@1421: for (typename Graph::UndirEdgeIt it(graph); it != INVALID; ++it) { deba@1421: nodeIdWriter->write(os, graph.source(it)); deba@1421: os << '\t'; deba@1421: nodeIdWriter->write(os, graph.target(it)); deba@1421: os << '\t'; deba@1421: if (forceIdMap) { deba@1421: os << graph.id(it) << '\t'; deba@1421: } deba@1421: for (int i = 0; i < (int)writers.size(); ++i) { deba@1421: writers[i].second->write(os, it); deba@1421: os << '\t'; deba@1421: } deba@1421: os << std::endl; deba@1421: } deba@1421: } deba@1421: deba@1421: public: deba@1421: deba@1421: /// \brief Returns true if the undirected edgeset can write the ids of deba@1421: /// the edges. deba@1421: /// deba@1421: /// Returns true if the undirected edgeset can write the ids of the deba@1421: /// undirected edges. It is possible only if an "id" named map was deba@1421: /// written or the \c _forceIdMap constructor parameter was true. deba@1421: bool isIdWriter() const { deba@1421: return forceIdMap || idMap != 0; deba@1421: } deba@1421: deba@1421: /// \brief Write the id of the given undirected edge. deba@1421: /// deba@1421: /// It writes the id of the given undirected edge. If there was written deba@1421: /// an "id" named map then it will write the map value belongs to the deba@1421: /// undirected edge. Otherwise if the \c forceId parameter was true it deba@1421: /// will write its id in the graph. deba@1429: void writeId(std::ostream& os, const UndirEdge& item) const { deba@1429: if (forceIdMap) { deba@1429: os << graph.id(item); deba@1429: } else { deba@1429: idMap->write(os, item); deba@1429: } deba@1429: } deba@1429: deba@1429: /// \brief Write the id of the given edge. deba@1429: /// deba@1429: /// It writes the id of the given edge. If there was written deba@1429: /// an "id" named map then it will write the map value belongs to the deba@1429: /// edge. Otherwise if the \c forceId parameter was true it deba@1429: /// will write its id in the graph. If the edge is forward map deba@1429: /// then its prefix character is \c '+' elsewhere \c '-'. deba@1429: void writeId(std::ostream& os, const Edge& item) const { deba@1690: if (graph.direction(item)) { deba@1429: os << "+ "; deba@1429: } else { deba@1429: os << "- "; deba@1429: } deba@1421: if (forceIdMap) { deba@1421: os << graph.id(item); deba@1421: } else { deba@1421: idMap->write(os, item); deba@1421: } deba@1421: } deba@1421: deba@1421: private: deba@1421: deba@1429: typedef std::vector*> > MapWriters; deba@1421: MapWriters writers; deba@1421: deba@1429: WriterBase* idMap; deba@1421: bool forceIdMap; deba@1421: deba@1705: const Graph& graph; deba@1409: std::string id; deba@1409: deba@1429: std::auto_ptr > nodeIdWriter; deba@1409: }; deba@1409: deba@1409: /// \ingroup io_group deba@1409: /// \brief SectionWriter for writing labeled nodes. deba@1409: /// deba@1409: /// The nodes section's header line is \c \@nodes \c nodes_id, but the deba@1409: /// \c nodes_id may be empty. deba@1409: /// deba@1409: /// Each line in the section contains the label of the node and deba@1409: /// then the node id. deba@1409: /// deba@1409: /// \relates LemonWriter deba@1409: template deba@1409: class NodeWriter : public CommonSectionWriterBase { deba@1409: typedef CommonSectionWriterBase Parent; deba@1409: typedef _Graph Graph; deba@1429: typedef typename Graph::Node Node; deba@1409: public: deba@1409: deba@1409: /// \brief Constructor. deba@1409: /// deba@1409: /// Constructor for NodeWriter. It creates the NodeWriter and deba@1409: /// attach it into the given LemonWriter. The given \c _IdWriter deba@1409: /// will write the nodes' id what can be a nodeset writer. deba@1409: template deba@1409: NodeWriter(LemonWriter& _writer, const _IdWriter& _idWriter, deba@1409: const std::string& _id = std::string()) deba@1476: : Parent(_writer), id(_id) { deba@1476: checkConcept<_writer_bits::ItemIdWriter, _IdWriter>(); deba@1476: idWriter.reset(new IdWriter(_idWriter)); deba@1476: } deba@1476: deba@1409: deba@1409: /// \brief Destructor. deba@1409: /// deba@1409: /// Destructor for NodeWriter. deba@1409: virtual ~NodeWriter() {} deba@1409: deba@1409: private: deba@1409: NodeWriter(const NodeWriter&); deba@1409: void operator=(const NodeWriter&); deba@1409: deba@1409: public: deba@1409: deba@1409: /// \brief Add a node writer command for the NodeWriter. deba@1409: /// deba@1409: /// Add a node writer command for the NodeWriter. deba@1429: void writeNode(const std::string& name, const Node& item) { deba@1409: writers.push_back(make_pair(name, &item)); deba@1409: } deba@1409: deba@1409: protected: deba@1409: deba@1409: /// \brief Header checking function. deba@1409: /// deba@1421: /// It gives back true when the header line start with \c \@nodes, deba@1409: /// and the header line's id and the writer's id are the same. deba@1409: virtual std::string header() { deba@1409: return "@nodes " + id; deba@1409: } deba@1409: deba@1409: /// \brief Writer function of the section. deba@1409: /// deba@1409: /// Write the content of the section. deba@1409: virtual void write(std::ostream& os) { deba@1476: if (!idWriter->isIdWriter()) { deba@1476: throw DataFormatError("Cannot find nodeset or ID map"); deba@1476: } deba@1409: for (int i = 0; i < (int)writers.size(); ++i) { deba@1409: os << writers[i].first << ' '; deba@1409: idWriter->write(os, *(writers[i].second)); deba@1409: os << std::endl; deba@1409: } deba@1409: } deba@1409: deba@1409: private: deba@1409: deba@1409: std::string id; deba@1409: deba@1429: typedef std::vector > NodeWriters; deba@1429: NodeWriters writers; deba@1429: std::auto_ptr > idWriter; deba@1409: }; deba@1409: deba@1409: /// \ingroup io_group deba@1421: /// \brief SectionWriter for writing labeled edges. deba@1409: /// deba@1409: /// The edges section's header line is \c \@edges \c edges_id, but the deba@1409: /// \c edges_id may be empty. deba@1409: /// deba@1409: /// Each line in the section contains the label of the edge and deba@1409: /// then the edge id. deba@1409: /// deba@1409: /// \relates LemonWriter deba@1409: template deba@1409: class EdgeWriter : public CommonSectionWriterBase { deba@1409: typedef CommonSectionWriterBase Parent; deba@1409: typedef _Graph Graph; deba@1429: typedef typename Graph::Edge Edge; deba@1409: public: deba@1409: deba@1409: /// \brief Constructor. deba@1409: /// deba@1409: /// Constructor for EdgeWriter. It creates the EdgeWriter and deba@1409: /// attach it into the given LemonWriter. The given \c _IdWriter deba@1409: /// will write the edges' id what can be a edgeset writer. deba@1409: template deba@1409: EdgeWriter(LemonWriter& _writer, const _IdWriter& _idWriter, deba@1409: const std::string& _id = std::string()) deba@1476: : Parent(_writer), id(_id) { deba@1476: checkConcept<_writer_bits::ItemIdWriter, _IdWriter>(); deba@1476: idWriter.reset(new IdWriter(_idWriter)); deba@1476: } deba@1409: deba@1409: /// \brief Destructor. deba@1409: /// deba@1409: /// Destructor for EdgeWriter. deba@1409: virtual ~EdgeWriter() {} deba@1409: private: deba@1409: EdgeWriter(const EdgeWriter&); deba@1409: void operator=(const EdgeWriter&); deba@1409: deba@1409: public: deba@1409: deba@1421: /// \brief Add an edge writer command for the EdgeWriter. deba@1409: /// deba@1421: /// Add an edge writer command for the EdgeWriter. deba@1429: void writeEdge(const std::string& name, const Edge& item) { deba@1409: writers.push_back(make_pair(name, &item)); deba@1409: } deba@1409: deba@1409: protected: deba@1409: deba@1409: /// \brief Header checking function. deba@1409: /// deba@1421: /// It gives back true when the header line start with \c \@edges, deba@1421: /// and the header line's id and the writer's id are the same. deba@1421: virtual std::string header() { deba@1421: return "@edges " + id; deba@1421: } deba@1421: deba@1421: /// \brief Writer function of the section. deba@1421: /// deba@1421: /// Write the content of the section. deba@1421: virtual void write(std::ostream& os) { deba@1476: if (!idWriter->isIdWriter()) { deba@1476: throw DataFormatError("Cannot find edgeset or ID map"); deba@1476: } deba@1421: for (int i = 0; i < (int)writers.size(); ++i) { deba@1421: os << writers[i].first << ' '; deba@1421: idWriter->write(os, *(writers[i].second)); deba@1421: os << std::endl; deba@1421: } deba@1421: } deba@1421: deba@1421: private: deba@1421: deba@1421: std::string id; deba@1421: deba@1429: typedef std::vector > EdgeWriters; deba@1429: EdgeWriters writers; deba@1421: deba@1429: std::auto_ptr > idWriter; deba@1421: }; deba@1421: deba@1421: /// \ingroup io_group deba@1421: /// \brief SectionWriter for writing labeled undirected edges. deba@1421: /// deba@1421: /// The undirected edges section's header line is \c \@undiredges deba@1421: /// \c undiredges_id, but the \c undiredges_id may be empty. deba@1421: /// deba@1421: /// Each line in the section contains the label of the undirected edge and deba@1421: /// then the undirected edge id. deba@1421: /// deba@1421: /// \relates LemonWriter deba@1421: template deba@1421: class UndirEdgeWriter : public CommonSectionWriterBase { deba@1421: typedef CommonSectionWriterBase Parent; deba@1421: typedef _Graph Graph; deba@1429: typedef typename Graph::Node Node; deba@1429: typedef typename Graph::Edge Edge; deba@1429: typedef typename Graph::UndirEdge UndirEdge; deba@1421: public: deba@1421: deba@1421: /// \brief Constructor. deba@1421: /// deba@1421: /// Constructor for UndirEdgeWriter. It creates the UndirEdgeWriter and deba@1421: /// attach it into the given LemonWriter. The given \c _IdWriter deba@1421: /// will write the undirected edges' id what can be an undirected deba@1421: /// edgeset writer. deba@1421: template deba@1421: UndirEdgeWriter(LemonWriter& _writer, const _IdWriter& _idWriter, deba@1421: const std::string& _id = std::string()) deba@1476: : Parent(_writer), id(_id) { deba@1476: checkConcept<_writer_bits::ItemIdWriter, _IdWriter>(); deba@1476: checkConcept<_writer_bits::ItemIdWriter, _IdWriter>(); deba@1476: undirEdgeIdWriter.reset(new IdWriter(_idWriter)); deba@1476: edgeIdWriter.reset(new IdWriter(_idWriter)); deba@1476: } deba@1421: deba@1421: /// \brief Destructor. deba@1421: /// deba@1421: /// Destructor for UndirEdgeWriter. deba@1421: virtual ~UndirEdgeWriter() {} deba@1421: private: deba@1421: UndirEdgeWriter(const UndirEdgeWriter&); deba@1421: void operator=(const UndirEdgeWriter&); deba@1421: deba@1421: public: deba@1421: deba@1429: /// \brief Add an edge writer command for the UndirEdgeWriter. deba@1429: /// deba@1429: /// Add an edge writer command for the UndirEdgeWriter. deba@1429: void writeEdge(const std::string& name, const Edge& item) { deba@1429: edgeWriters.push_back(make_pair(name, &item)); deba@1429: } deba@1429: deba@1421: /// \brief Add an undirected edge writer command for the UndirEdgeWriter. deba@1421: /// deba@1429: /// Add an undirected edge writer command for the UndirEdgeWriter. deba@1429: void writeUndirEdge(const std::string& name, const UndirEdge& item) { deba@1429: undirEdgeWriters.push_back(make_pair(name, &item)); deba@1421: } deba@1421: deba@1421: protected: deba@1421: deba@1421: /// \brief Header checking function. deba@1421: /// deba@1421: /// It gives back true when the header line start with \c \@undiredges, deba@1409: /// and the header line's id and the writer's id are the same. deba@1409: virtual std::string header() { deba@1429: return "@undiredges " + id; deba@1409: } deba@1409: deba@1409: /// \brief Writer function of the section. deba@1409: /// deba@1409: /// Write the content of the section. deba@1409: virtual void write(std::ostream& os) { deba@1476: if (!edgeIdWriter->isIdWriter()) { deba@1476: throw DataFormatError("Cannot find undirected edgeset or ID map"); deba@1476: } deba@1476: if (!undirEdgeIdWriter->isIdWriter()) { deba@1476: throw DataFormatError("Cannot find undirected edgeset or ID map"); deba@1476: } deba@1429: for (int i = 0; i < (int)undirEdgeWriters.size(); ++i) { deba@1429: os << undirEdgeWriters[i].first << ' '; deba@1429: undirEdgeIdWriter->write(os, *(undirEdgeWriters[i].second)); deba@1429: os << std::endl; deba@1429: } deba@1429: for (int i = 0; i < (int)edgeWriters.size(); ++i) { deba@1429: os << edgeWriters[i].first << ' '; deba@1429: edgeIdWriter->write(os, *(edgeWriters[i].second)); deba@1409: os << std::endl; deba@1409: } deba@1409: } deba@1409: deba@1409: private: deba@1409: deba@1409: std::string id; deba@1409: deba@1429: typedef std::vector > UndirEdgeWriters; deba@1429: UndirEdgeWriters undirEdgeWriters; deba@1429: std::auto_ptr > undirEdgeIdWriter; deba@1409: deba@1429: typedef std::vector > EdgeWriters; deba@1429: EdgeWriters edgeWriters; deba@1429: std::auto_ptr > edgeIdWriter; deba@1429: deba@1409: }; deba@1409: deba@1409: /// \ingroup io_group deba@1409: /// \brief SectionWriter for attributes. deba@1409: /// deba@1409: /// The lemon format can store multiple attribute set. Each set has deba@1409: /// the header line \c \@attributes \c attributeset_id, but the deba@1409: /// attributeset_id may be empty. deba@1409: /// deba@1409: /// The attributeset section contains several lines. Each of them starts deba@1409: /// with the name of attribute and then the value. deba@1409: /// deba@1409: /// \relates LemonWriter deba@1409: template deba@1409: class AttributeWriter : public CommonSectionWriterBase { deba@1409: typedef CommonSectionWriterBase Parent; deba@1409: typedef _Traits Traits; deba@1409: public: deba@1409: /// \brief Constructor. deba@1409: /// deba@1409: /// Constructor for AttributeWriter. It creates the AttributeWriter and deba@1409: /// attach it into the given LemonWriter. deba@1409: AttributeWriter(LemonWriter& _writer, deba@1409: const std::string& _id = std::string()) deba@1409: : Parent(_writer), id(_id) {} deba@1409: deba@1409: /// \brief Destructor. deba@1409: /// deba@1409: /// Destructor for AttributeWriter. deba@1409: virtual ~AttributeWriter() { deba@1409: typename Writers::iterator it; deba@1409: for (it = writers.begin(); it != writers.end(); ++it) { deba@1409: delete it->second; deba@1409: } deba@1409: } deba@1409: deba@1409: private: deba@1409: AttributeWriter(const AttributeWriter&); deba@1409: void operator=(AttributeWriter&); deba@1409: deba@1409: public: deba@1409: /// \brief Add an attribute writer command for the writer. deba@1409: /// deba@1409: /// Add an attribute writer command for the writer. deba@1409: template deba@1409: AttributeWriter& writeAttribute(const std::string& id, deba@1409: const Value& value) { deba@1409: return deba@1409: writeAttribute >(id, value); deba@1409: } deba@1409: deba@1409: /// \brief Add an attribute writer command for the writer. deba@1409: /// deba@1409: /// Add an attribute writer command for the writer. deba@1409: template deba@1409: AttributeWriter& writeAttribute(const std::string& name, deba@1409: const Value& value, deba@1409: const Writer& writer = Writer()) { deba@1492: checkConcept<_writer_bits::ItemWriter, Writer>(); deba@1409: writers.push_back(make_pair(name, new ValueWriter deba@1409: (value, writer))); deba@1409: return *this; deba@1409: } deba@1409: deba@1409: protected: deba@1409: deba@1409: /// \brief The header of section. deba@1409: /// deba@1409: /// It gives back the header of the section. deba@1409: std::string header() { deba@1409: return "@attributes " + id; deba@1409: } deba@1409: deba@1409: /// \brief Writer function of the section. deba@1409: /// deba@1409: /// Write the content of the section. deba@1409: void write(std::ostream& os) { deba@1409: typename Writers::iterator it; deba@1409: for (it = writers.begin(); it != writers.end(); ++it) { deba@1409: os << it->first << ' '; deba@1409: it->second->write(os); deba@1409: os << std::endl; deba@1409: } deba@1409: } deba@1409: deba@1409: private: deba@1409: std::string id; deba@1409: deba@1409: typedef std::vector > Writers; deba@1409: Writers writers; deba@1409: }; deba@1409: deba@1409: deba@1409: } deba@1409: #endif