1 // Copyright (C) 2006  Tiago de Paula Peixoto <tiago@forked.de>
2 // Copyright (C) 2004,2009  The Trustees of Indiana University.
3 //
4 // Use, modification and distribution is subject to the Boost Software
5 // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
6 // http://www.boost.org/LICENSE_1_0.txt)
7 
8 //  Authors: Douglas Gregor
9 //           Jeremiah Willcock
10 //           Andrew Lumsdaine
11 //           Tiago de Paula Peixoto
12 
13 #define BOOST_GRAPH_SOURCE
14 #include <boost/foreach.hpp>
15 #include <boost/optional.hpp>
16 #include <boost/throw_exception.hpp>
17 #include <boost/graph/graphml.hpp>
18 #include <boost/graph/dll_import_export.hpp>
19 #include <boost/property_tree/ptree.hpp>
20 #include <boost/property_tree/xml_parser.hpp>
21 
22 using namespace boost;
23 
24 namespace {
25 
26 class graphml_reader
27 {
28 public:
graphml_reader(mutate_graph & g)29     graphml_reader(mutate_graph& g)
30         : m_g(g) { }
31 
path(const std::string & str)32     static boost::property_tree::ptree::path_type path(const std::string& str) {
33       return boost::property_tree::ptree::path_type(str, '/');
34     }
35 
get_graphs(const boost::property_tree::ptree & top,size_t desired_idx,bool is_root,std::vector<const boost::property_tree::ptree * > & result)36     void get_graphs(const boost::property_tree::ptree& top,
37                            size_t desired_idx /* or -1 for all */, bool is_root,
38                            std::vector<const boost::property_tree::ptree*>& result) {
39       using boost::property_tree::ptree;
40       size_t current_idx = 0;
41       bool is_first = is_root;
42       BOOST_FOREACH(const ptree::value_type& n, top) {
43         if (n.first == "graph") {
44           if (current_idx == desired_idx || desired_idx == (size_t)(-1)) {
45             result.push_back(&n.second);
46             if(is_first)
47             {
48               is_first = false;
49               BOOST_FOREACH(const ptree::value_type& attr, n.second) {
50                 if (attr.first != "data")
51                   continue;
52                 std::string key = attr.second.get<std::string>(path("<xmlattr>/key"));
53                 std::string value = attr.second.get_value("");
54                 handle_graph_property(key, value);
55               }
56             }
57 
58             get_graphs(n.second, (size_t)(-1), false, result);
59             if (desired_idx != (size_t)(-1)) break;
60           }
61           ++current_idx;
62         }
63       }
64     }
65 
run(std::istream & in,size_t desired_idx)66     void run(std::istream& in, size_t desired_idx)
67     {
68       using boost::property_tree::ptree;
69       ptree pt;
70       read_xml(in, pt, boost::property_tree::xml_parser::no_comments | boost::property_tree::xml_parser::trim_whitespace);
71       ptree gml = pt.get_child(path("graphml"));
72       // Search for attributes
73       BOOST_FOREACH(const ptree::value_type& child, gml) {
74         if (child.first != "key") continue;
75         std::string id = child.second.get(path("<xmlattr>/id"), "");
76         std::string for_ = child.second.get(path("<xmlattr>/for"), "");
77         std::string name = child.second.get(path("<xmlattr>/attr.name"), "");
78         std::string type = child.second.get(path("<xmlattr>/attr.type"), "");
79         key_kind kind = all_key;
80         if (for_ == "graph") kind = graph_key;
81         else if (for_ == "node") kind = node_key;
82         else if (for_ == "edge") kind = edge_key;
83         else if (for_ == "hyperedge") kind = hyperedge_key;
84         else if (for_ == "port") kind = port_key;
85         else if (for_ == "endpoint") kind = endpoint_key;
86         else if (for_ == "all") kind = all_key;
87         else if (for_ == "graphml") kind = graphml_key;
88         else {BOOST_THROW_EXCEPTION(parse_error("Attribute for is not valid: " + for_));}
89         m_keys[id] = kind;
90         m_key_name[id] = name;
91         m_key_type[id] = type;
92         boost::optional<std::string> default_ = child.second.get_optional<std::string>(path("default"));
93         if (default_) m_key_default[id] = default_.get();
94       }
95       // Search for graphs
96       std::vector<const ptree*> graphs;
97       handle_graph();
98       get_graphs(gml, desired_idx, true, graphs);
99       BOOST_FOREACH(const ptree* gr, graphs) {
100         // Search for nodes
101         BOOST_FOREACH(const ptree::value_type& node, *gr) {
102           if (node.first != "node") continue;
103           std::string id = node.second.get<std::string>(path("<xmlattr>/id"));
104           handle_vertex(id);
105           BOOST_FOREACH(const ptree::value_type& attr, node.second) {
106             if (attr.first != "data") continue;
107             std::string key = attr.second.get<std::string>(path("<xmlattr>/key"));
108             std::string value = attr.second.get_value("");
109             handle_node_property(key, id, value);
110           }
111         }
112       }
113       BOOST_FOREACH(const ptree* gr, graphs) {
114         bool default_directed = gr->get<std::string>(path("<xmlattr>/edgedefault")) == "directed";
115         // Search for edges
116         BOOST_FOREACH(const ptree::value_type& edge, *gr) {
117           if (edge.first != "edge") continue;
118           std::string source = edge.second.get<std::string>(path("<xmlattr>/source"));
119           std::string target = edge.second.get<std::string>(path("<xmlattr>/target"));
120           std::string local_directed = edge.second.get(path("<xmlattr>/directed"), "");
121           bool is_directed = (local_directed == "" ? default_directed : local_directed == "true");
122           if (is_directed != m_g.is_directed()) {
123             if (is_directed) {
124               BOOST_THROW_EXCEPTION(directed_graph_error());
125             } else {
126               BOOST_THROW_EXCEPTION(undirected_graph_error());
127             }
128           }
129           size_t old_edges_size = m_edge.size();
130           handle_edge(source, target);
131           BOOST_FOREACH(const ptree::value_type& attr, edge.second) {
132             if (attr.first != "data") continue;
133             std::string key = attr.second.get<std::string>(path("<xmlattr>/key"));
134             std::string value = attr.second.get_value("");
135             handle_edge_property(key, old_edges_size, value);
136           }
137         }
138       }
139     }
140 
141 private:
142     /// The kinds of keys. Not all of these are supported
143     enum key_kind {
144         graph_key,
145         node_key,
146         edge_key,
147         hyperedge_key,
148         port_key,
149         endpoint_key,
150         all_key,
151         graphml_key
152     };
153 
154     void
handle_vertex(const std::string & v)155     handle_vertex(const std::string& v)
156     {
157         bool is_new = false;
158 
159         if (m_vertex.find(v) == m_vertex.end())
160         {
161             m_vertex[v] = m_g.do_add_vertex();
162             is_new = true;
163         }
164 
165         if (is_new)
166         {
167             std::map<std::string, std::string>::iterator iter;
168             for (iter = m_key_default.begin(); iter != m_key_default.end(); ++iter)
169             {
170                 if (m_keys[iter->first] == node_key)
171                     handle_node_property(iter->first, v, iter->second);
172             }
173         }
174     }
175 
176     any
get_vertex_descriptor(const std::string & v)177     get_vertex_descriptor(const std::string& v)
178     {
179       return m_vertex[v];
180     }
181 
182     void
handle_edge(const std::string & u,const std::string & v)183     handle_edge(const std::string& u, const std::string& v)
184     {
185         handle_vertex(u);
186         handle_vertex(v);
187 
188         any source, target;
189         source = get_vertex_descriptor(u);
190         target = get_vertex_descriptor(v);
191 
192         any edge;
193         bool added;
194         boost::tie(edge, added) = m_g.do_add_edge(source, target);
195         if (!added) {
196             BOOST_THROW_EXCEPTION(bad_parallel_edge(u, v));
197         }
198 
199         size_t e = m_edge.size();
200         m_edge.push_back(edge);
201 
202         std::map<std::string, std::string>::iterator iter;
203         for (iter = m_key_default.begin(); iter != m_key_default.end(); ++iter)
204         {
205             if (m_keys[iter->first] == edge_key)
206                 handle_edge_property(iter->first, e, iter->second);
207         }
208     }
209 
210     void
handle_graph()211     handle_graph()
212     {
213       std::map<std::string, std::string>::iterator iter;
214       for (iter = m_key_default.begin(); iter != m_key_default.end(); ++iter)
215       {
216         if (m_keys[iter->first] == graph_key)
217           handle_graph_property(iter->first, iter->second);
218       }
219     }
220 
handle_graph_property(const std::string & key_id,const std::string & value)221     void handle_graph_property(const std::string& key_id, const std::string& value)
222     {
223       m_g.set_graph_property(m_key_name[key_id], value, m_key_type[key_id]);
224     }
225 
handle_node_property(const std::string & key_id,const std::string & descriptor,const std::string & value)226     void handle_node_property(const std::string& key_id, const std::string& descriptor, const std::string& value)
227     {
228       m_g.set_vertex_property(m_key_name[key_id], m_vertex[descriptor], value, m_key_type[key_id]);
229     }
230 
handle_edge_property(const std::string & key_id,size_t descriptor,const std::string & value)231     void handle_edge_property(const std::string& key_id, size_t descriptor, const std::string& value)
232     {
233       m_g.set_edge_property(m_key_name[key_id], m_edge[descriptor], value, m_key_type[key_id]);
234     }
235 
236     mutate_graph& m_g;
237     std::map<std::string, key_kind> m_keys;
238     std::map<std::string, std::string> m_key_name;
239     std::map<std::string, std::string> m_key_type;
240     std::map<std::string, std::string> m_key_default;
241     std::map<std::string, any> m_vertex;
242     std::vector<any> m_edge;
243 };
244 
245 }
246 
247 namespace boost
248 {
249 void BOOST_GRAPH_DECL
read_graphml(std::istream & in,mutate_graph & g,size_t desired_idx)250 read_graphml(std::istream& in, mutate_graph& g, size_t desired_idx)
251 {
252     graphml_reader reader(g);
253     reader.run(in, desired_idx);
254 }
255 }
256