| // Copyright (C) 2006 Tiago de Paula Peixoto <tiago@forked.de> |
| // Copyright (C) 2004,2009 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 |
| // Jeremiah Willcock |
| // Andrew Lumsdaine |
| // Tiago de Paula Peixoto |
| |
| #define BOOST_GRAPH_SOURCE |
| #include <boost/foreach.hpp> |
| #include <boost/optional.hpp> |
| #include <boost/throw_exception.hpp> |
| #include <boost/graph/graphml.hpp> |
| #include <boost/graph/dll_import_export.hpp> |
| #include <boost/property_tree/ptree.hpp> |
| #include <boost/property_tree/xml_parser.hpp> |
| |
| using namespace boost; |
| |
| class graphml_reader |
| { |
| public: |
| graphml_reader(mutate_graph& g) |
| : m_g(g) { } |
| |
| static boost::property_tree::ptree::path_type path(const std::string& str) { |
| return boost::property_tree::ptree::path_type(str, '/'); |
| } |
| |
| static void get_graphs(const boost::property_tree::ptree& top, |
| std::vector<const boost::property_tree::ptree*>& result) { |
| using boost::property_tree::ptree; |
| BOOST_FOREACH(const ptree::value_type& n, top) { |
| if (n.first == "graph") { |
| result.push_back(&n.second); |
| get_graphs(n.second, result); |
| } |
| } |
| } |
| |
| void run(std::istream& in) |
| { |
| using boost::property_tree::ptree; |
| ptree pt; |
| read_xml(in, pt, boost::property_tree::xml_parser::no_comments | boost::property_tree::xml_parser::trim_whitespace); |
| ptree gml = pt.get_child(path("graphml")); |
| // Search for attributes |
| BOOST_FOREACH(const ptree::value_type& child, gml) { |
| if (child.first != "key") continue; |
| std::string id = child.second.get(path("<xmlattr>/id"), ""); |
| std::string for_ = child.second.get(path("<xmlattr>/for"), ""); |
| std::string name = child.second.get(path("<xmlattr>/attr.name"), ""); |
| std::string type = child.second.get(path("<xmlattr>/attr.type"), ""); |
| key_kind kind = all_key; |
| if (for_ == "graph") kind = graph_key; |
| else if (for_ == "node") kind = node_key; |
| else if (for_ == "edge") kind = edge_key; |
| else if (for_ == "hyperedge") kind = hyperedge_key; |
| else if (for_ == "port") kind = port_key; |
| else if (for_ == "endpoint") kind = endpoint_key; |
| else if (for_ == "all") kind = all_key; |
| else {BOOST_THROW_EXCEPTION(parse_error("Attribute for is not valid: " + for_));} |
| m_keys[id] = kind; |
| m_key_name[id] = name; |
| m_key_type[id] = type; |
| boost::optional<std::string> default_ = child.second.get_optional<std::string>(path("default")); |
| if (default_) m_key_default[id] = default_.get(); |
| } |
| // Search for graphs |
| std::vector<const ptree*> graphs; |
| get_graphs(gml, graphs); |
| BOOST_FOREACH(const ptree* gr, graphs) { |
| // Search for nodes |
| BOOST_FOREACH(const ptree::value_type& node, *gr) { |
| if (node.first != "node") continue; |
| std::string id = node.second.get<std::string>(path("<xmlattr>/id")); |
| handle_vertex(id); |
| BOOST_FOREACH(const ptree::value_type& attr, node.second) { |
| if (attr.first != "data") continue; |
| std::string key = attr.second.get<std::string>(path("<xmlattr>/key")); |
| std::string value = attr.second.get_value(""); |
| handle_node_property(key, id, value); |
| } |
| } |
| } |
| BOOST_FOREACH(const ptree* gr, graphs) { |
| bool default_directed = gr->get<std::string>(path("<xmlattr>/edgedefault")) == "directed"; |
| // Search for edges |
| BOOST_FOREACH(const ptree::value_type& edge, *gr) { |
| if (edge.first != "edge") continue; |
| std::string id = edge.second.get<std::string>(path("<xmlattr>/id")); |
| std::string source = edge.second.get<std::string>(path("<xmlattr>/source")); |
| std::string target = edge.second.get<std::string>(path("<xmlattr>/target")); |
| std::string local_directed = edge.second.get(path("<xmlattr>/directed"), ""); |
| bool is_directed = (local_directed == "" ? default_directed : local_directed == "true"); |
| if (is_directed != m_g.is_directed()) { |
| if (is_directed) { |
| BOOST_THROW_EXCEPTION(directed_graph_error()); |
| } else { |
| BOOST_THROW_EXCEPTION(undirected_graph_error()); |
| } |
| } |
| size_t old_edges_size = m_edge.size(); |
| handle_edge(source, target); |
| BOOST_FOREACH(const ptree::value_type& attr, edge.second) { |
| if (attr.first != "data") continue; |
| std::string key = attr.second.get<std::string>(path("<xmlattr>/key")); |
| std::string value = attr.second.get_value(""); |
| handle_edge_property(key, old_edges_size, value); |
| } |
| } |
| } |
| } |
| |
| private: |
| /// The kinds of keys. Not all of these are supported |
| enum key_kind { |
| graph_key, |
| node_key, |
| edge_key, |
| hyperedge_key, |
| port_key, |
| endpoint_key, |
| all_key |
| }; |
| |
| void |
| handle_vertex(const std::string& v) |
| { |
| bool is_new = false; |
| |
| if (m_vertex.find(v) == m_vertex.end()) |
| { |
| m_vertex[v] = m_g.do_add_vertex(); |
| is_new = true; |
| } |
| |
| if (is_new) |
| { |
| std::map<std::string, std::string>::iterator iter; |
| for (iter = m_key_default.begin(); iter != m_key_default.end(); ++iter) |
| { |
| if (m_keys[iter->first] == node_key) |
| handle_node_property(iter->first, v, iter->second); |
| } |
| } |
| } |
| |
| any |
| get_vertex_descriptor(const std::string& v) |
| { |
| return m_vertex[v]; |
| } |
| |
| void |
| handle_edge(const std::string& u, const std::string& v) |
| { |
| handle_vertex(u); |
| handle_vertex(v); |
| |
| any source, target; |
| source = get_vertex_descriptor(u); |
| target = get_vertex_descriptor(v); |
| |
| any edge; |
| bool added; |
| boost::tie(edge, added) = m_g.do_add_edge(source, target); |
| if (!added) { |
| BOOST_THROW_EXCEPTION(bad_parallel_edge(u, v)); |
| } |
| |
| size_t e = m_edge.size(); |
| m_edge.push_back(edge); |
| |
| std::map<std::string, std::string>::iterator iter; |
| for (iter = m_key_default.begin(); iter != m_key_default.end(); ++iter) |
| { |
| if (m_keys[iter->first] == edge_key) |
| handle_edge_property(iter->first, e, iter->second); |
| } |
| } |
| |
| void handle_node_property(const std::string& key_id, const std::string& descriptor, const std::string& value) |
| { |
| m_g.set_vertex_property(m_key_name[key_id], m_vertex[descriptor], value, m_key_type[key_id]); |
| } |
| |
| void handle_edge_property(const std::string& key_id, size_t descriptor, const std::string& value) |
| { |
| m_g.set_edge_property(m_key_name[key_id], m_edge[descriptor], value, m_key_type[key_id]); |
| } |
| |
| mutate_graph& m_g; |
| std::map<std::string, key_kind> m_keys; |
| std::map<std::string, std::string> m_key_name; |
| std::map<std::string, std::string> m_key_type; |
| std::map<std::string, std::string> m_key_default; |
| std::map<std::string, any> m_vertex; |
| std::vector<any> m_edge; |
| }; |
| |
| namespace boost |
| { |
| void BOOST_GRAPH_DECL |
| read_graphml(std::istream& in, mutate_graph& g) |
| { |
| graphml_reader reader(g); |
| reader.run(in); |
| } |
| } |