| // Copyright (C) 2006 Tiago de Paula Peixoto <tiago@forked.de> |
| // Copyright (C) 2004 The Trustees of Indiana University. |
| // |
| // Use, modification and distribution is subject to the Boost Software |
| // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| // |
| // Authors: Douglas Gregor |
| // Andrew Lumsdaine |
| // Tiago de Paula Peixoto |
| |
| #ifndef BOOST_GRAPH_GRAPHML_HPP |
| #define BOOST_GRAPH_GRAPHML_HPP |
| |
| #include <boost/config.hpp> |
| #include <boost/lexical_cast.hpp> |
| #include <boost/any.hpp> |
| #include <boost/type_traits/is_convertible.hpp> |
| #include <boost/graph/dll_import_export.hpp> |
| #include <boost/graph/graphviz.hpp> // for exceptions |
| #include <typeinfo> |
| #include <boost/mpl/bool.hpp> |
| #include <boost/mpl/vector.hpp> |
| #include <boost/mpl/find.hpp> |
| #include <boost/mpl/for_each.hpp> |
| #include <boost/property_tree/detail/xml_parser_utils.hpp> |
| #include <boost/throw_exception.hpp> |
| #include <exception> |
| #include <sstream> |
| |
| namespace boost |
| { |
| |
| ///////////////////////////////////////////////////////////////////////////// |
| // Graph reader exceptions |
| ///////////////////////////////////////////////////////////////////////////// |
| struct parse_error: public graph_exception |
| { |
| parse_error(const std::string& err) {error = err; statement = "parse error: " + error;} |
| virtual ~parse_error() throw() {} |
| virtual const char* what() const throw() {return statement.c_str();} |
| std::string statement; |
| std::string error; |
| }; |
| |
| |
| class mutate_graph |
| { |
| public: |
| virtual ~mutate_graph() {} |
| virtual bool is_directed() const = 0; |
| |
| virtual boost::any do_add_vertex() = 0; |
| virtual std::pair<boost::any,bool> do_add_edge(boost::any source, boost::any target) = 0; |
| |
| virtual void |
| set_graph_property(const std::string& name, const std::string& value, const std::string& value_type) = 0; |
| |
| virtual void |
| set_vertex_property(const std::string& name, boost::any vertex, const std::string& value, const std::string& value_type) = 0; |
| |
| virtual void |
| set_edge_property(const std::string& name, boost::any edge, const std::string& value, const std::string& value_type) = 0; |
| }; |
| |
| template<typename MutableGraph> |
| class mutate_graph_impl : public mutate_graph |
| { |
| typedef typename graph_traits<MutableGraph>::vertex_descriptor vertex_descriptor; |
| typedef typename graph_traits<MutableGraph>::edge_descriptor edge_descriptor; |
| |
| public: |
| mutate_graph_impl(MutableGraph& g, dynamic_properties& dp) |
| : m_g(g), m_dp(dp) { } |
| |
| bool is_directed() const |
| { |
| return is_convertible<typename graph_traits<MutableGraph>::directed_category, |
| directed_tag>::value; |
| } |
| |
| virtual any do_add_vertex() |
| { |
| return any(add_vertex(m_g)); |
| } |
| |
| virtual std::pair<any,bool> do_add_edge(any source, any target) |
| { |
| std::pair<edge_descriptor,bool> retval = add_edge(any_cast<vertex_descriptor>(source), |
| any_cast<vertex_descriptor>(target), m_g); |
| return std::make_pair(any(retval.first), retval.second); |
| } |
| |
| virtual void |
| set_graph_property(const std::string& name, const std::string& value, const std::string& value_type) |
| { |
| bool type_found = false; |
| try |
| { |
| mpl::for_each<value_types>(put_property<MutableGraph,value_types> |
| (name, m_dp, m_g, value, value_type, m_type_names, type_found)); |
| } |
| catch (bad_lexical_cast) |
| { |
| BOOST_THROW_EXCEPTION( |
| parse_error("invalid value \"" + value + "\" for key " + |
| name + " of type " + value_type)); |
| } |
| if (!type_found) |
| { |
| BOOST_THROW_EXCEPTION( |
| parse_error("unrecognized type \"" + value_type + |
| "\" for key " + name)); |
| } |
| |
| } |
| |
| virtual void |
| set_vertex_property(const std::string& name, any vertex, const std::string& value, const std::string& value_type) |
| { |
| bool type_found = false; |
| try |
| { |
| mpl::for_each<value_types>(put_property<vertex_descriptor,value_types> |
| (name, m_dp, any_cast<vertex_descriptor>(vertex), |
| value, value_type, m_type_names, type_found)); |
| } |
| catch (bad_lexical_cast) |
| { |
| BOOST_THROW_EXCEPTION( |
| parse_error("invalid value \"" + value + "\" for key " + |
| name + " of type " + value_type)); |
| } |
| if (!type_found) |
| { |
| BOOST_THROW_EXCEPTION( |
| parse_error("unrecognized type \"" + value_type + |
| "\" for key " + name)); |
| } |
| |
| } |
| |
| virtual void |
| set_edge_property(const std::string& name, any edge, const std::string& value, const std::string& value_type) |
| { |
| bool type_found = false; |
| try |
| { |
| mpl::for_each<value_types>(put_property<edge_descriptor,value_types> |
| (name, m_dp, any_cast<edge_descriptor>(edge), |
| value, value_type, m_type_names, type_found)); |
| } |
| catch (bad_lexical_cast) |
| { |
| BOOST_THROW_EXCEPTION( |
| parse_error("invalid value \"" + value + "\" for key " + |
| name + " of type " + value_type)); |
| } |
| if (!type_found) |
| { |
| BOOST_THROW_EXCEPTION( |
| parse_error("unrecognized type \"" + value_type + |
| "\" for key " + name)); |
| } |
| } |
| |
| template <typename Key, typename ValueVector> |
| class put_property |
| { |
| public: |
| put_property(const std::string& name, dynamic_properties& dp, const Key& key, |
| const std::string& value, const std::string& value_type, |
| const char** type_names, bool& type_found) |
| : m_name(name), m_dp(dp), m_key(key), m_value(value), |
| m_value_type(value_type), m_type_names(type_names), |
| m_type_found(type_found) {} |
| template <class Value> |
| void operator()(Value) |
| { |
| if (m_value_type == m_type_names[mpl::find<ValueVector,Value>::type::pos::value]) |
| { |
| put(m_name, m_dp, m_key, lexical_cast<Value>(m_value)); |
| m_type_found = true; |
| } |
| } |
| private: |
| const std::string& m_name; |
| dynamic_properties& m_dp; |
| const Key& m_key; |
| const std::string& m_value; |
| const std::string& m_value_type; |
| const char** m_type_names; |
| bool& m_type_found; |
| }; |
| |
| protected: |
| MutableGraph& m_g; |
| dynamic_properties& m_dp; |
| typedef mpl::vector<bool, int, long, float, double, std::string> value_types; |
| static const char* m_type_names[]; |
| }; |
| |
| template<typename MutableGraph> |
| const char* mutate_graph_impl<MutableGraph>::m_type_names[] = {"boolean", "int", "long", "float", "double", "string"}; |
| |
| void BOOST_GRAPH_DECL |
| read_graphml(std::istream& in, mutate_graph& g); |
| |
| template<typename MutableGraph> |
| void |
| read_graphml(std::istream& in, MutableGraph& g, dynamic_properties& dp) |
| { |
| mutate_graph_impl<MutableGraph> mg(g,dp); |
| read_graphml(in, mg); |
| } |
| |
| template <typename Types> |
| class get_type_name |
| { |
| public: |
| get_type_name(const std::type_info& type, const char** type_names, std::string& type_name) |
| : m_type(type), m_type_names(type_names), m_type_name(type_name) {} |
| template <typename Type> |
| void operator()(Type) |
| { |
| if (typeid(Type) == m_type) |
| m_type_name = m_type_names[mpl::find<Types,Type>::type::pos::value]; |
| } |
| private: |
| const std::type_info &m_type; |
| const char** m_type_names; |
| std::string &m_type_name; |
| }; |
| |
| |
| template <typename Graph, typename VertexIndexMap> |
| void |
| write_graphml(std::ostream& out, const Graph& g, VertexIndexMap vertex_index, |
| const dynamic_properties& dp, bool ordered_vertices=false) |
| { |
| typedef typename graph_traits<Graph>::directed_category directed_category; |
| typedef typename graph_traits<Graph>::edge_descriptor edge_descriptor; |
| typedef typename graph_traits<Graph>::vertex_descriptor vertex_descriptor; |
| |
| using boost::property_tree::xml_parser::encode_char_entities; |
| |
| BOOST_STATIC_CONSTANT(bool, |
| graph_is_directed = |
| (is_convertible<directed_category*, directed_tag*>::value)); |
| |
| out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" |
| << "<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">\n"; |
| |
| typedef mpl::vector<bool, short, unsigned short, int, unsigned int, long, unsigned long, long long, unsigned long long, float, double, long double, std::string> value_types; |
| const char* type_names[] = {"boolean", "int", "int", "int", "int", "long", "long", "long", "long", "float", "double", "double", "string"}; |
| std::map<std::string, std::string> graph_key_ids; |
| std::map<std::string, std::string> vertex_key_ids; |
| std::map<std::string, std::string> edge_key_ids; |
| int key_count = 0; |
| |
| // Output keys |
| for (dynamic_properties::const_iterator i = dp.begin(); i != dp.end(); ++i) |
| { |
| std::string key_id = "key" + lexical_cast<std::string>(key_count++); |
| if (i->second->key() == typeid(Graph)) |
| vertex_key_ids[i->first] = key_id; |
| else if (i->second->key() == typeid(vertex_descriptor)) |
| vertex_key_ids[i->first] = key_id; |
| else if (i->second->key() == typeid(edge_descriptor)) |
| edge_key_ids[i->first] = key_id; |
| else |
| continue; |
| std::string type_name = "string"; |
| mpl::for_each<value_types>(get_type_name<value_types>(i->second->value(), type_names, type_name)); |
| out << " <key id=\"" << encode_char_entities(key_id) << "\" for=\"" |
| << (i->second->key() == typeid(Graph) ? "graph" : (i->second->key() == typeid(vertex_descriptor) ? "node" : "edge")) << "\"" |
| << " attr.name=\"" << i->first << "\"" |
| << " attr.type=\"" << type_name << "\"" |
| << " />\n"; |
| } |
| |
| out << " <graph id=\"G\" edgedefault=\"" |
| << (graph_is_directed ? "directed" : "undirected") << "\"" |
| << " parse.nodeids=\"" << (ordered_vertices ? "canonical" : "free") << "\"" |
| << " parse.edgeids=\"canonical\" parse.order=\"nodesfirst\">\n"; |
| |
| // Output graph data |
| for (dynamic_properties::const_iterator i = dp.begin(); i != dp.end(); ++i) |
| { |
| if (i->second->key() == typeid(Graph)) |
| { |
| out << " <data key=\"" << graph_key_ids[i->first] << "\">" |
| << encode_char_entities(i->second->get_string(g)) << "</data>\n"; |
| } |
| } |
| |
| typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator; |
| vertex_iterator v, v_end; |
| for (boost::tie(v, v_end) = vertices(g); v != v_end; ++v) |
| { |
| out << " <node id=\"n" << get(vertex_index, *v) << "\">\n"; |
| // Output data |
| for (dynamic_properties::const_iterator i = dp.begin(); i != dp.end(); ++i) |
| { |
| if (i->second->key() == typeid(vertex_descriptor)) |
| { |
| out << " <data key=\"" << vertex_key_ids[i->first] << "\">" |
| << encode_char_entities(i->second->get_string(*v)) << "</data>\n"; |
| } |
| } |
| out << " </node>\n"; |
| } |
| |
| typedef typename graph_traits<Graph>::edge_iterator edge_iterator; |
| edge_iterator e, e_end; |
| typename graph_traits<Graph>::edges_size_type edge_count = 0; |
| for (boost::tie(e, e_end) = edges(g); e != e_end; ++e) |
| { |
| out << " <edge id=\"e" << edge_count++ << "\" source=\"n" |
| << get(vertex_index, source(*e, g)) << "\" target=\"n" |
| << get(vertex_index, target(*e, g)) << "\">\n"; |
| |
| // Output data |
| for (dynamic_properties::const_iterator i = dp.begin(); i != dp.end(); ++i) |
| { |
| if (i->second->key() == typeid(edge_descriptor)) |
| { |
| out << " <data key=\"" << edge_key_ids[i->first] << "\">" |
| << encode_char_entities(i->second->get_string(*e)) << "</data>\n"; |
| } |
| } |
| out << " </edge>\n"; |
| } |
| |
| out << " </graph>\n" |
| << "</graphml>\n"; |
| } |
| |
| |
| template <typename Graph> |
| void |
| write_graphml(std::ostream& out, const Graph& g, const dynamic_properties& dp, |
| bool ordered_vertices=false) |
| { |
| write_graphml(out, g, get(vertex_index, g), dp, ordered_vertices); |
| } |
| |
| } // boost namespace |
| |
| #endif // BOOST_GRAPH_GRAPHML_HPP |