1#ifndef GRAPHML_WRITER_HPP
2#define GRAPHML_WRITER_HPP
3
4#include "Graph/map_graph.hpp"
5#include <iostream>
6#include <fstream>
7#include "util/common.hpp"
8
9
10/*! \brief Create properties name starting from a type T
11 *
12 * if T has defined some properties name that name are used otherwise
13 * default name are created
14 *
15 * \tparam T vertex type
16 *
17 */
18
19template <typename T>
20void create_prop(std::string * str)
21{
22 // if T has attributes defined
23 if (has_attributes<T>::value )
24 {
25 // Create properties names based on the attributes name defined
26 for (size_t i = 0 ; i < T::max_prop ; i++)
27 {
28 str[i] = std::string(T::attributes::name[i]);
29 }
30 }
31 else
32 {
33 // Create default properties name
34 for (size_t i = 0 ; i < T::max_prop ; i++)
35 {
36 str[i] = "attr" + std::to_string(i);
37 }
38 }
39}
40
41/*! \brief this class is a functor for "for_each" algorithm
42 *
43 * This class is a functor for "for_each" algorithm. For each
44 * element of the boost::vector the operator() is called.
45 * Is mainly used to create a string containing all the vertex
46 * properties
47 *
48 */
49
50template<typename G>
51struct vertex_prop
52{
53 //! Properties counter
54 int cnt = 0;
55
56 //! vertex properties
57 std::string & v_prop;
58
59 //! Attribute names
60 std::string * attributes_names;
61
62 //! Number of attributes name defined into the vertex
63 int n_attr = 0;
64
65 //! indicate if attributes_names is to destroy
66 bool to_destroy = false;
67
68 /*! \brief Constructor
69 *
70 * Create a vertex properties list
71 *
72 * \param v_prop std::string that is filled with the graph properties in the GraphML format
73 * \param a_name array with the names of the properties
74 *
75 */
76 vertex_prop(std::string & v_prop, typename G::V_type::attributes & a_name)
77 :v_prop(v_prop),attributes_names(a_name.name)
78 {
79 // Calculate the number of attributes name
80 n_attr = sizeof(a_name.name)/sizeof(std::string);
81 };
82
83 /*! \brief Constructor
84 *
85 * Create a vertex properties list
86 *
87 * \param v_prop std::string that is filled with the graph properties in the GraphML format
88 *
89 */
90 vertex_prop(std::string & v_prop)
91 :v_prop(v_prop),attributes_names(NULL)
92 {
93 // Calculate the number of attributes
94 n_attr = G::V_type::max_prop;
95
96 // Create default property names
97 attributes_names = new std::string[G::V_type::max_prop];
98 to_destroy = true;
99
100 // Create default property names
101 create_prop<typename G::V_type>(attributes_names);
102 };
103
104 //! destructor
105 ~vertex_prop()
106 {
107 if (to_destroy == true)
108 {delete [] attributes_names;}
109 }
110
111 /*! It call the functor for each member
112 *
113 * \param t each member
114 *
115 */
116 template<typename T>
117 void operator()(T& t)
118 {
119 //! Create an entry for the attribute
120 if (cnt < n_attr)
121 {
122 // if it is a yFile extension property name, does not process it
123 if (attributes_names[cnt] == "x" || attributes_names[cnt] == "y"
124 || attributes_names[cnt] == "z" || attributes_names[cnt] == "shape" )
125 {cnt++; return ;}
126
127 // Create a property string based on the type of the property
128 if (std::is_same<T,float>::value)
129 v_prop += "<key id=\"vk" + std::to_string(cnt) + "\" for=\"node\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"float\"/>\n";
130 else if (std::is_same<T,double>::value)
131 v_prop += "<key id=\"vk" + std::to_string(cnt) + "\" for=\"node\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"double\"/>\n";
132 else if (std::is_same<T,int>::value)
133 v_prop += "<key id=\"vk" + std::to_string(cnt) + "\" for=\"node\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"int\"/>\n";
134 else if (std::is_same<T,long int>::value)
135 v_prop += "<key id=\"vk" + std::to_string(cnt) + "\" for=\"node\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"long\"/>\n";
136 else if (std::is_same<T,bool>::value)
137 v_prop += "<key id=\"vk" + std::to_string(cnt) + "\" for=\"node\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"boolean\"/>\n";
138 else if (std::is_same<T,std::string>::value)
139 v_prop += "<key id=\"vk" + std::to_string(cnt) + "\" for=\"node\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"string\"/>\n";
140 }
141
142 cnt++;
143 }
144};
145
146/*! \brief this class is a functor for "for_each" algorithm
147 *
148 * This class is a functor for "for_each" algorithm. For each
149 * element of the boost::vector the operator() is called.
150 * Is mainly used to create a string containing all the vertex
151 * properties
152 *
153 */
154
155template<typename G>
156struct vertex_node
157{
158 //! Vertex object container
159 const typename G::V_container & vo;
160
161 //! Properties counter
162 int cnt = 0;
163
164 //! vertex node string
165 std::string & v_node;
166
167 //! Attribute names
168 std::string * attributes_names;
169
170 //! Number of attributes name defined into the vertex
171 int n_attr = 0;
172
173 //! indicate if attributes_names is to destroy
174 bool to_destroy = false;
175
176 /*! \brief Constructor
177 *
178 * Create a vertex node
179 *
180 * \param v_node std::string that is filled with the graph node definition in the GraphML format
181 * \param n_obj object container to access its properties for example encapc<...>
182 * \param a_name stub SFINAE, it basically check if G has properties names defined, if yes this
183 * constructor is selected over the other one
184 *
185 */
186 inline vertex_node(std::string & v_node, const typename G::V_container & n_obj, typename G::V_type::attributes & a_name)
187 :vo(n_obj),v_node(v_node),attributes_names(a_name.name)
188 {
189 // Calculate the number of attributes name
190 n_attr = sizeof(a_name.name)/sizeof(std::string);
191 };
192
193#ifdef DEBUG
194 /*! \brief Constructor
195 *
196 * Calling this constructor produce an error. This class store the reference of the object,
197 * this mean that the object passed must not be a temporal object
198 *
199 */
200 inline vertex_node(std::string & v_node, const typename G::V_container && n_obj, typename G::V_type::attributes & a_name)
201 :vo(n_obj),v_node(v_node),attributes_names(a_name.name)
202 {std::cerr << "Error: " <<__FILE__ << ":" << __LINE__ << " Passing a temporal object\n";};
203#endif
204
205 /*! \brief Constructor
206 *
207 * Create a vertex properties list
208 *
209 * \param v_node std::string that is filled with the graph properties in the GraphML format
210 * \param n_obj object container to access its properties for example encapc<...>
211 *
212 */
213 inline vertex_node(std::string & v_node, const typename G::V_container & n_obj)
214 :vo(n_obj),v_node(v_node),attributes_names(NULL)
215 {
216 // Calculate the number of attributes
217 n_attr = G::V_type::max_prop;
218
219 // Create default property names
220 attributes_names = new std::string[G::V_type::max_prop];
221 to_destroy = true;
222
223 // Create default property names
224 create_prop<typename G::V_type>(attributes_names);
225 };
226
227 inline ~vertex_node()
228 {
229 if (to_destroy == true)
230 {delete [] attributes_names;}
231 }
232
233#ifdef DEBUG
234 /*! \brief Constructor
235 *
236 * Calling this constructor produce an error. This class store the reference of the object,
237 * this mean that the object passed must not be a temporal object
238 *
239 */
240 inline vertex_node(std::string & v_node, const typename G::V_container && n_obj)
241 :vo(n_obj),v_node(v_node),attributes_names(NULL)
242 {std::cerr << "Error: " <<__FILE__ << ":" << __LINE__ << " Passing a temporal object\n";};
243#endif
244
245 /*! \brief Create a new node
246 *
247 * \param v_c id of the node
248 *
249 */
250 void new_node(size_t v_c)
251 {
252 // start a new node
253 v_node += "<node id=\"n"+ std::to_string(v_c) + "\">\n";
254
255 // reset the counter properties
256 cnt = 0;
257 }
258
259 /*! \brief Close a node
260 *
261 * Close a node
262 *
263 */
264 void end_node()
265 {
266 // close a node
267 v_node += "</node>\n";
268 }
269
270 /*! \brief It call the functor for each member
271 *
272 * \param t each member
273 *
274 */
275 template<typename T>
276 void operator()(T& t)
277 {
278 //! Create an entry for the attribute
279 if (T::value < n_attr)
280 {
281 typedef typename std::remove_reference<decltype(vo.template get<T::value>())>::type type_get;
282
283 // Create a property string based on the type of the property
284 if (std::is_same<type_get,float>::value)
285 v_node += " <data key=\"vk" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
286 else if (std::is_same<type_get,double>::value)
287 v_node += " <data key=\"vk" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
288 else if (std::is_same<type_get,int>::value )
289 v_node += " <data key=\"vk" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
290 else if (std::is_same<type_get,long int>::value)
291 v_node += " <data key=\"vk" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
292 else if (std::is_same<type_get,bool>::value)
293 v_node += " <data key=\"vk" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
294 else if (std::is_same<type_get,std::string>::value)
295 v_node += " <data key=\"vk" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
296 }
297
298 cnt++;
299 }
300};
301
302/*! \brief this class is a functor for "for_each" algorithm
303 *
304 * This class is a functor for "for_each" algorithm. For each
305 * element of the boost::vector the operator() is called.
306 * Is mainly used to create a string containing all the edge
307 * properties
308 *
309 */
310
311template<typename G>
312struct edge_prop
313{
314 //! Properties counter
315 int cnt = 0;
316
317 //! edge properties
318 std::string & e_prop;
319
320 //! Attribute names
321 std::string * attributes_names;
322
323 //! Number of attributes name defined into the vertex
324 int n_attr = 0;
325
326 //! indicate if attributes_names is to destroy
327 bool to_destroy = false;
328
329 /*! \brief Constructor
330 *
331 * Create an edge properties list
332 *
333 * \param e_prop std::string that is filled with the graph properties in the GraphML format
334 * \param a_name stub SFINAE, it basically check if G::E_type has properties names defined, if yes this
335 * constructor is selected over the other one
336 *
337 */
338 edge_prop(std::string & e_prop, typename G::E_type::attributes & a_name)
339 :e_prop(e_prop),attributes_names(a_name.name)
340 {
341 // Calculate the number of attributes name
342 n_attr = sizeof(a_name.name)/sizeof(std::string);
343 };
344
345 /*! \brief Constructor
346 *
347 * Create an edge properties list
348 *
349 * \param e_prop std::string that is filled with the graph properties in the GraphML format
350 *
351 */
352 edge_prop(std::string & e_prop)
353 :e_prop(e_prop),attributes_names(NULL)
354 {
355 // Calculate the number of attributes
356 n_attr = G::E_type::max_prop;
357
358 // Create default property names
359 attributes_names = new std::string[G::E_type::max_prop];
360 to_destroy = true;
361
362 // Create default property names
363 create_prop<typename G::E_type>(attributes_names);
364 };
365
366 inline ~edge_prop()
367 {
368 if (to_destroy == true)
369 {delete [] attributes_names;}
370 }
371
372 /*! \brief It call the functor for each member
373 *
374 * \param t each member
375 *
376 */
377 template<typename T>
378 void operator()(T& t)
379 {
380 //! Create an entry for the attribute
381 if (cnt < n_attr)
382 {
383 // Create a property string based on the type of the property
384 if (std::is_same<T,float>::value)
385 e_prop += "<key id=\"ek" + std::to_string(cnt) + "\" for=\"edge\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"float\"/>\n";
386 else if (std::is_same<T,double>::value)
387 e_prop += "<key id=\"ek" + std::to_string(cnt) + "\" for=\"edge\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"double\"/>\n";
388 else if (std::is_same<T,int>::value)
389 e_prop += "<key id=\"ek" + std::to_string(cnt) + "\" for=\"edge\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"int\"/>\n";
390 else if (std::is_same<T,long int>::value)
391 e_prop += "<key id=\"ek" + std::to_string(cnt) + "\" for=\"edge\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"long\"/>\n";
392 else if (std::is_same<T,bool>::value)
393 e_prop += "<key id=\"ek" + std::to_string(cnt) + "\" for=\"edge\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"boolean\"/>\n";
394 else if (std::is_same<T,std::string>::value)
395 e_prop += "<key id=\"ek" + std::to_string(cnt) + "\" for=\"edge\" attr.name=\"" + attributes_names[cnt] + "\" attr.type=\"string\"/>\n";
396 }
397
398 cnt++;
399 }
400};
401
402/*! \brief this class is a functor for "for_each" algorithm
403 *
404 * This class is a functor for "for_each" algorithm. For each
405 * element of the boost::vector the operator() is called.
406 * Is mainly used to create a string containing all the edge
407 * properties
408 *
409 */
410
411template<typename G>
412struct edge_node
413{
414 //! Vertex object container
415 typename G::E_container & vo;
416
417 //! Properties counter
418 int cnt = 0;
419
420 //! edge node string
421 std::string & e_node;
422
423 //! Attribute names
424 std::string * attributes_names;
425
426 //! Number of attributes name defined into the vertex
427 int n_attr = 0;
428
429 //! indicate if attributes_names is to destroy
430 bool to_destroy = false;
431
432 /*! \brief Constructor
433 *
434 * Create an edge node
435 *
436 * \param e_node std::string that is filled with the graph node definition in the GraphML format
437 * \param n_obj object container to access the object properties for example encapc<...>
438 * \param a_name attributes names
439 *
440 */
441 edge_node(std::string & e_node, typename G::E_container & n_obj, typename G::E_type::attributes & a_name)
442 :vo(n_obj),e_node(e_node),attributes_names(a_name.name)
443 {
444 // Calculate the number of attributes name
445 n_attr = sizeof(a_name.name)/sizeof(std::string);
446 };
447
448 /*! \brief Constructor
449 *
450 * Create an edge node
451 *
452 * \param e_node std::string that is filled with the graph properties in the GraphML format
453 * \param n_obj object container to access the object properties for example encapc<...>
454 *
455 */
456 edge_node(std::string & e_node, typename G::E_container & n_obj)
457 :vo(n_obj),e_node(e_node),attributes_names(NULL)
458 {
459 // Calculate the number of attributes
460 n_attr = G::E_type::max_prop;
461
462 // Create a number of default properties name
463 attributes_names = new std::string[G::E_type::max_prop];
464 to_destroy = true;
465
466 // Create default property names
467 create_prop<typename G::E_type>(attributes_names);
468
469 };
470
471 inline ~edge_node()
472 {
473 if (to_destroy == true)
474 {delete [] attributes_names;}
475 }
476
477 /*! \brief Create a new node
478 *
479 * \param v_c node number
480 * \param s source node id
481 * \param d destination node id
482 *
483 */
484 void new_node(size_t v_c, size_t s, size_t d)
485 {
486 // start a new node
487 e_node += "<edge id=\"e"+ std::to_string(v_c) + "\" source=\"n" + std::to_string(s) + "\" target=\"n" + std::to_string(d) + "\">\n";
488
489 // reset the counter properties
490 cnt = 0;
491 }
492
493 /*! \brief Close a node
494 *
495 * Close a node
496 *
497 */
498 void end_node()
499 {
500 // close a node
501 e_node += "</edge>\n";
502 }
503
504 /*! It call the functor for each member
505 *
506 * \param t each member
507 *
508 */
509 template<typename T>
510 void operator()(T& t)
511 {
512 //! Create an entry for the attribute
513 if (T::value < n_attr)
514 {
515 typedef typename std::remove_reference<decltype(vo.template get<T::value>())>::type type_get;
516
517 // Create a property string based on the type of the property
518 if (std::is_same<type_get,float>::value)
519 e_node += " <data key=\"ek" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
520 else if (std::is_same<type_get,double>::value)
521 e_node += " <data key=\"ek" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
522 else if (std::is_same<type_get,int>::value)
523 e_node += " <data key=\"ek" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
524 else if (std::is_same<type_get,long int>::value)
525 e_node += " <data key=\"ek" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
526 else if (std::is_same<type_get,bool>::value)
527 e_node += " <data key=\"ek" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
528 else if (std::is_same<type_get,std::string>::value)
529 e_node += " <data key=\"ek" + std::to_string(cnt) + "\">" + std::to_string(vo.template get<T::value>()) + "</data>\n";
530 }
531
532 cnt++;
533 }
534};
535
536/*!
537 *
538 * From a Graphbasic structure it write a GraphML format file
539 *
540 */
541
542template <typename Graph>
543class GraphMLWriter
544{
545 //! Graph to write
546 Graph & g;
547
548 /*! \brief It get the vertex properties list
549 *
550 * It get the vertex properties list of the vertex defined as a GraphML header
551 * and
552 * define position and shape of the node
553 *
554 * \return a string that define the vertex properties in graphML format
555 *
556 */
557 std::string get_vertex_properties_list()
558 {
559 //! vertex property output string
560 std::string v_out("");
561
562 // create a vertex property functor
563 vertex_prop<Graph> vp(v_out);
564
565 // Iterate through all the vertex and create the vertex list
566 boost::mpl::for_each_ref< typename Graph::V_type::type >(vp);
567
568 // return the vertex properties string
569 return v_out;
570 }
571
572 /*! \brief It get the edge properties list
573 *
574 * It get the edge properties list of the edge defined as a GraphML header
575 *
576 * \return a string that define the edge properties in graphML format
577 *
578 */
579 std::string get_edge_properties_list()
580 {
581 //! edge property output string
582 std::string e_out;
583
584 // create a vertex property functor
585 edge_prop<Graph> ep(e_out);
586
587 // Iterate through all the vertex and create the vertex list
588 boost::mpl::for_each_ref< typename Graph::E_type::type >(ep);
589
590 // return the edge properties string
591 return e_out;
592 }
593
594 /*! \brief Get the string containing the set of vertices
595 *
596 * \return the set of vertices as string
597 *
598 */
599 std::string get_vertex_list()
600 {
601 // node counter
602 size_t nc = 0;
603
604 //! vertex node output string
605 std::string v_out;
606
607 //! Get a vertex iterator
608 auto it = g.getVertexIterator();
609
610 // if there is the next element
611 while (it.isNext())
612 {
613 auto v = g.vertex(it.get());
614
615 // create a vertex list functor
616 vertex_node<Graph> vn(v_out,v);
617
618 // create new node
619 vn.new_node(nc);
620
621 // Iterate through all the vertex and create the vertex list
622 boost::mpl::for_each_ref< boost::mpl::range_c<int,0,Graph::V_type::max_prop> >(vn);
623
624 // end node
625 vn.end_node();
626
627 // increment the iterator and counter
628 ++it;
629 nc++;
630 }
631
632 // return the vertex list
633 return v_out;
634 }
635
636 /*! \brief return the edge list as a string
637 *
638 * \return the edge list as string
639 *
640 */
641 std::string get_edge_list()
642 {
643 // node counter
644 size_t nc = 0;
645
646 //! edge node output string
647 std::string e_out;
648
649 //! Get an edge iterator
650 auto it = g.getEdgeIterator();
651
652 // if there is the next element
653 while (it.isNext())
654 {
655 // Get the edge object
656 auto obj = g.edge(it.get());
657
658 // create an edge list functor
659 edge_node<Graph> en(e_out,obj);
660
661 // create a new node
662 en.new_node(nc,it.source(),it.target());
663
664 // Iterate through all the edges and create the edge list
665 boost::mpl::for_each_ref< boost::mpl::range_c<int,0,Graph::E_type::max_prop> >(en);
666
667 // end new node
668 en.end_node();
669
670 // increment the operator
671 ++it;
672 nc++;
673 }
674
675 // return the edge list
676 return e_out;
677 }
678
679public:
680
681 /*!
682 *
683 * GraphMLWriter constructor, it take a graph and write a GraphML format
684 *
685 * \param g Graph to write
686 *
687 */
688 GraphMLWriter(Graph & g)
689 :g(g)
690 {}
691
692 /*! \brief It write a GraphML file from a graph
693 *
694 * \param file path where to write
695 * \param name of the graph
696 *
697 */
698 bool write(std::string file, std::string graph_name="Graph")
699 {
700 // Header for the GraphML
701 std::string gml_header;
702 // Vertex list of the GraphML
703 std::string vertex_list;
704 // End for the GraphML
705 std::string gml_header_end;
706 // Graph header
707 std::string graph_header;
708 // Graph header end
709 std::string graph_header_end;
710 // Edge list of the GraphML
711 std::string edge_list;
712 // vertex properties header
713 std::string vertex_prop_header;
714 // edge properties header
715 std::string edge_prop_header;
716
717 // GraphML header
718 gml_header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\
719 <graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\"\n\
720 xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n\
721 xsi:schemaLocation=\"http://graphml.graphdrawing.org/xmlns\n\
722 http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd\">\n";
723
724 // Graph header to define an header
725 graph_header = "<graph id=\"" + graph_name + "\" edgedefault=\"undirected\">\n";
726 // Graph header end
727 graph_header_end = "</graph>\n";
728
729 // Vertex properties header
730 vertex_prop_header = get_vertex_properties_list();
731
732 // Edge properties header
733 edge_prop_header = get_edge_properties_list();
734
735 // Get the node graph list
736 vertex_list = get_vertex_list();
737
738 // Get the edge graph list
739 edge_list = get_edge_list();
740
741 // Header end
742 gml_header_end = "</graphml>";
743
744 // write the file
745
746 std::ofstream ofs(file);
747
748 // Check if the file is open
749 if (ofs.is_open() == false)
750 {std::cerr << "Error cannot creare the graphML file: " + file;}
751
752 ofs << gml_header << graph_header << vertex_prop_header << edge_prop_header <<
753 vertex_list << edge_list << graph_header_end << gml_header_end;
754
755 // Close the file
756
757 ofs.close();
758
759 // Completed succefully
760 return true;
761 }
762};
763
764#endif
765