1 /** \file
2 * \brief Implements GraphML write functionality of class GraphIO.
3 *
4 * \author Łukasz Hanuszczak
5 *
6 * \par License:
7 * This file is part of the Open Graph Drawing Framework (OGDF).
8 *
9 * \par
10 * Copyright (C)<br>
11 * See README.md in the OGDF root directory for details.
12 *
13 * \par
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * Version 2 or 3 as published by the Free Software Foundation;
17 * see the file LICENSE.txt included in the packaging of this file
18 * for details.
19 *
20 * \par
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * \par
27 * You should have received a copy of the GNU General Public
28 * License along with this program; if not, see
29 * http://www.gnu.org/copyleft/gpl.html
30 */
31
32 #include <ogdf/fileformats/GraphIO.h>
33 #include <ogdf/fileformats/GraphML.h>
34 #include <ogdf/lib/pugixml/pugixml.h>
35
36
37 namespace ogdf {
38
39
writeGraphMLHeader(pugi::xml_document & doc)40 static inline pugi::xml_node writeGraphMLHeader(pugi::xml_document &doc)
41 {
42 const std::string xmlns = "http://graphml.graphdrawing.org/xmlns";
43
44 pugi::xml_node rootNode = doc.append_child("graphml");
45 rootNode.append_attribute("xmlns") = xmlns.c_str();
46 rootNode.append_attribute("xmlns:xsi") = "http://www.w3.org/2001/XMLSchema-instance";
47 rootNode.append_attribute("xsi:schemaLocation") = (xmlns + "\n" + xmlns + "/1.0/graphml.xsd\">\n").c_str();
48
49 return rootNode;
50 }
51
writeGraphTag(pugi::xml_node xmlNode,std::string edgeDefault)52 static inline pugi::xml_node writeGraphTag(pugi::xml_node xmlNode, std::string edgeDefault)
53 {
54 pugi::xml_node graphNode = xmlNode.append_child("graph");
55 graphNode.append_attribute("id") = "G";
56 graphNode.append_attribute("edgedefault") = edgeDefault.c_str();
57
58 return graphNode;
59
60 }
61
62
defineGraphMLAttribute(pugi::xml_node xmlNode,const std::string & kind,const std::string & name,const std::string & type)63 static inline void defineGraphMLAttribute(
64 pugi::xml_node xmlNode,
65 const std::string &kind,
66 const std::string &name,
67 const std::string &type)
68 {
69 pugi::xml_node key = xmlNode.append_child("key");
70 key.append_attribute("for") = kind.c_str();
71 key.append_attribute("attr.name") = name.c_str();
72 key.append_attribute("attr.type") = type.c_str();
73 key.append_attribute("id") = name.c_str();
74 }
75
76
defineGraphMLAttributes(pugi::xml_node xmlNode,long attributes)77 static inline void defineGraphMLAttributes(pugi::xml_node xmlNode, long attributes)
78 {
79 using namespace graphml;
80
81 // Gephi-compatible attributes
82 if(attributes & GraphAttributes::nodeLabel) {
83 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeLabel), "string");
84 }
85
86 if(attributes & GraphAttributes::nodeLabelPosition) {
87 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeLabelX), "float");
88 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeLabelY), "float");
89 if (attributes & GraphAttributes::threeD) {
90 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeLabelZ), "float");
91 }
92 }
93
94 if(attributes & GraphAttributes::nodeGraphics) {
95 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::X), "double");
96 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::Y), "double");
97 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::Size), "double");
98 }
99
100 if(attributes & GraphAttributes::nodeStyle) {
101 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::R), "int");
102 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::G), "int");
103 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::B), "int");
104 }
105
106 if(attributes & GraphAttributes::edgeLabel) {
107 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeLabel), "string");
108 }
109
110 if(attributes & GraphAttributes::edgeDoubleWeight) {
111 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeWeight), "double");
112 } else if(attributes & GraphAttributes::edgeIntWeight) {
113 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeWeight), "int");
114 }
115
116 // OGDF-specific attributes.
117 if (attributes & GraphAttributes::nodeGraphics) {
118 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::Width), "double");
119 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::Height), "double");
120 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::Shape), "string");
121 }
122
123 if(attributes & GraphAttributes::nodeStyle) {
124 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeStrokeColor), "string");
125 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeStrokeType), "int");
126 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeStrokeWidth), "double");
127 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeFillPattern), "int");
128 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeFillBackground), "string");
129 }
130
131 if(attributes & GraphAttributes::nodeWeight) {
132 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeWeight), "int");
133 }
134
135 if(attributes & GraphAttributes::nodeType) {
136 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeType), "int");
137 }
138
139 if(attributes & GraphAttributes::nodeId) {
140 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::NodeId), "int");
141 }
142
143 if(attributes & GraphAttributes::nodeTemplate) {
144 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::Template), "string");
145 }
146
147 if(attributes & GraphAttributes::threeD) {
148 defineGraphMLAttribute(xmlNode, "node", toString(Attribute::Z), "double");
149 }
150
151 if(attributes & GraphAttributes::edgeGraphics) {
152 // Currently bending points are printed as list. More XML-ish way has
153 // to be adopted in future (it will probably involve writing custom
154 // XML schema...).
155 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeBends), "string");
156 }
157
158 if(attributes & GraphAttributes::edgeType) {
159 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeType), "string");
160 }
161
162 if(attributes & GraphAttributes::edgeArrow) {
163 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeArrow), "string");
164 }
165
166 if(attributes & GraphAttributes::edgeStyle) {
167 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeStrokeColor), "string");
168 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeStrokeType), "int");
169 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeStrokeWidth), "double");
170 }
171
172 if(attributes & GraphAttributes::edgeSubGraphs) {
173 // Similarly to bend points, print as list.
174 defineGraphMLAttribute(xmlNode, "edge", toString(Attribute::EdgeSubGraph), "string");
175 }
176 }
177
178
179 template <typename T>
writeGraphMLAttribute(pugi::xml_node xmlNode,const std::string & name,const T & value)180 static inline void writeGraphMLAttribute(
181 pugi::xml_node xmlNode,
182 const std::string &name,
183 const T &value)
184 {
185 pugi::xml_node data = xmlNode.append_child("data");
186 data.append_attribute("key") = name.c_str();
187 data.text() = value;
188 }
189
190
writeGraphMLNode(pugi::xml_node xmlNode,const node & v)191 static inline void writeGraphMLNode(pugi::xml_node xmlNode, const node &v)
192 {
193 xmlNode.append_child("node").append_attribute("id") = v->index();
194 }
195
196
writeGraphMLEdge(pugi::xml_node xmlNode,const edge & e)197 static inline pugi::xml_node writeGraphMLEdge(pugi::xml_node xmlNode, const edge &e)
198 {
199 pugi::xml_node edgeXmlNode = xmlNode.append_child("edge");
200 edgeXmlNode.append_attribute("id") = e->index();
201 edgeXmlNode.append_attribute("source") = e->source()->index();
202 edgeXmlNode.append_attribute("target") = e->target()->index();
203
204 return edgeXmlNode;
205 }
206
207
writeGraphMLNode(pugi::xml_node xmlNode,const GraphAttributes & GA,const node & v)208 static inline void writeGraphMLNode(
209 pugi::xml_node xmlNode,
210 const GraphAttributes &GA,
211 const node &v)
212 {
213 using namespace graphml;
214
215 pugi::xml_node nodeTag = xmlNode.append_child("node");
216
217 nodeTag.append_attribute("id") = v->index();
218
219 if(GA.has(GraphAttributes::nodeId)) {
220 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeId), GA.idNode(v));
221 }
222
223 if(GA.has(GraphAttributes::nodeLabel) && GA.label(v) != "") {
224 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeLabel), GA.label(v).c_str());
225 }
226
227 if(GA.has(GraphAttributes::nodeGraphics)) {
228 writeGraphMLAttribute(nodeTag, toString(Attribute::X), GA.x(v));
229 writeGraphMLAttribute(nodeTag, toString(Attribute::Y), GA.y(v));
230 writeGraphMLAttribute(nodeTag, toString(Attribute::Width), GA.width(v));
231 writeGraphMLAttribute(nodeTag, toString(Attribute::Height), GA.height(v));
232 writeGraphMLAttribute(nodeTag, toString(Attribute::Size), std::max(GA.width(v), GA.height(v)));
233 writeGraphMLAttribute(nodeTag, toString(Attribute::Shape), toString(GA.shape(v)).c_str());
234 }
235
236 if(GA.has(GraphAttributes::threeD)) {
237 writeGraphMLAttribute(nodeTag, toString(Attribute::Z), GA.z(v));
238 }
239
240 if(GA.has(GraphAttributes::nodeLabelPosition)) {
241 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeLabelX), GA.xLabel(v));
242 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeLabelY), GA.yLabel(v));
243 if(GA.has(GraphAttributes::threeD)) {
244 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeLabelZ), GA.zLabel(v));
245 }
246 }
247
248 if(GA.has(GraphAttributes::nodeStyle)) {
249 const Color &col = GA.fillColor(v);
250 writeGraphMLAttribute(nodeTag, toString(Attribute::R), col.red());
251 writeGraphMLAttribute(nodeTag, toString(Attribute::G), col.green());
252 writeGraphMLAttribute(nodeTag, toString(Attribute::B), col.blue());
253 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeFillPattern), int(GA.fillPattern(v)));
254 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeFillBackground), GA.fillBgColor(v).toString().c_str());
255
256 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeStrokeColor), GA.strokeColor(v).toString().c_str());
257 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeStrokeType), int(GA.strokeType(v)));
258 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeStrokeWidth), GA.strokeWidth(v));
259 }
260
261 if(GA.has(GraphAttributes::nodeType)) {
262 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeType), int(GA.type(v)));
263 }
264
265 if(GA.has(GraphAttributes::nodeTemplate) &&
266 GA.templateNode(v).length() > 0) {
267 writeGraphMLAttribute(nodeTag, toString(Attribute::Template), GA.templateNode(v).c_str());
268 }
269
270 if(GA.has(GraphAttributes::nodeWeight)) {
271 writeGraphMLAttribute(nodeTag, toString(Attribute::NodeWeight), GA.weight(v));
272 }
273 }
274
275
writeGraphMLEdge(pugi::xml_node xmlNode,const GraphAttributes & GA,const edge & e)276 static inline void writeGraphMLEdge(
277 pugi::xml_node xmlNode,
278 const GraphAttributes &GA,
279 const edge &e)
280 {
281 using namespace graphml;
282
283 pugi::xml_node edgeTag = writeGraphMLEdge(xmlNode, e);
284
285 if(GA.has(GraphAttributes::edgeLabel) && GA.label(e) != "") {
286 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeLabel), GA.label(e).c_str());
287 }
288
289 if(GA.has(GraphAttributes::edgeDoubleWeight)) {
290 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeWeight), GA.doubleWeight(e));
291 } else if(GA.has(GraphAttributes::edgeIntWeight)) {
292 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeWeight), GA.intWeight(e));
293 }
294
295 if(GA.has(GraphAttributes::edgeGraphics) && !GA.bends(e).empty()) {
296 std::stringstream sstream;
297 sstream.setf(std::ios::fixed);
298
299 for(const DPoint &p : GA.bends(e)) {
300 sstream << p.m_x << " " << p.m_y << " ";
301 }
302
303 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeBends), sstream.str().c_str());
304 }
305
306 if(GA.has(GraphAttributes::edgeType)) {
307 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeType), toString(GA.type(e)).c_str());
308 }
309
310 if(GA.has(GraphAttributes::edgeArrow)) {
311 const EdgeArrow &arrow = GA.arrowType(e);
312 if (arrow != EdgeArrow::Undefined) {
313 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeArrow), toString(arrow).c_str());
314 }
315 }
316
317 if(GA.has(GraphAttributes::edgeStyle)) {
318 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeStrokeColor), GA.strokeColor(e).toString().c_str());
319 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeStrokeType), int(GA.strokeType(e)));
320 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeStrokeWidth), GA.strokeWidth(e));
321 }
322
323 if(GA.has(GraphAttributes::edgeSubGraphs)) {
324 const uint32_t mask = GA.subGraphBits(e);
325
326 // Iterate over all subgraphs and print the ones the edge is part of.
327 std::stringstream sstream;
328 for (size_t sg = 0; sg < sizeof(mask) * 8; ++sg) {
329 if((1 << sg) & mask) {
330 sstream << (sg == 0 ? "" : " ") << sg;
331 }
332 }
333 writeGraphMLAttribute(edgeTag, toString(Attribute::EdgeSubGraph), sstream.str().c_str());
334 }
335 }
336
337
writeGraphMLCluster(pugi::xml_node xmlNode,const ClusterGraph & C,const cluster & c,int clusterId)338 static void writeGraphMLCluster(
339 pugi::xml_node xmlNode,
340 const ClusterGraph &C,
341 const cluster &c,
342 int clusterId)
343 {
344 pugi::xml_node graph;
345
346 if(C.rootCluster() != c) {
347 pugi::xml_node clusterXmlNode = xmlNode.append_child("node");
348 const char* idValue = ("cluster" + to_string(clusterId)).c_str();
349 clusterXmlNode.append_attribute("id") = idValue;
350
351 graph = clusterXmlNode.append_child("graph");
352 graph.append_attribute("id") = idValue;
353 graph.append_attribute("edgedefault") = "directed";
354 }
355
356 clusterId++;
357
358 for(cluster child : c->children) {
359 writeGraphMLCluster(graph, C, child, clusterId);
360 }
361
362 for(node v : c->nodes) {
363 writeGraphMLNode(graph, v);
364 }
365 }
366
367
writeGraphMLCluster(pugi::xml_node xmlNode,const ClusterGraphAttributes & CA,const cluster & c,int clusterId)368 static void writeGraphMLCluster(
369 pugi::xml_node xmlNode,
370 const ClusterGraphAttributes &CA,
371 const cluster &c,
372 int clusterId)
373 {
374 pugi::xml_node graph;
375 pugi::xml_node clusterTag;
376 const bool isRoot = CA.constClusterGraph().rootCluster() == c;
377
378 if(isRoot) {
379 graph = xmlNode;
380 } else {
381 clusterTag = xmlNode.append_child("node");
382 const char* idValue = ("cluster" + to_string(clusterId)).c_str();
383 clusterTag.append_attribute("id") = idValue;
384
385 pugi::xml_node graphXmlNode = clusterTag.append_child("graph");
386 graphXmlNode.append_attribute("id") = idValue;
387 graphXmlNode.append_attribute("edgedefault") = CA.directed() ? "directed" : "undirected";
388 }
389
390 clusterId++;
391
392 for(cluster child : c->children) {
393 writeGraphMLCluster(graph, CA, child, clusterId);
394 }
395
396 for(node v : c->nodes) {
397 writeGraphMLNode(graph, CA, v);
398 }
399
400 // There should be no attributes for root cluster.
401 if(isRoot) {
402 return;
403 }
404
405 using namespace graphml;
406
407 // Writing cluster attributes (defined as cluster-node attributes).
408 if(CA.label(c).length() > 0) {
409 writeGraphMLAttribute(clusterTag, toString(Attribute::NodeLabel), CA.label(c).c_str());
410 }
411 writeGraphMLAttribute(clusterTag, toString(Attribute::X), CA.x(c));
412 writeGraphMLAttribute(clusterTag, toString(Attribute::Y), CA.y(c));
413
414 const Color &col = CA.fillColor(c);
415 writeGraphMLAttribute(clusterTag, toString(Attribute::R), col.red());
416 writeGraphMLAttribute(clusterTag, toString(Attribute::G), col.green());
417 writeGraphMLAttribute(clusterTag, toString(Attribute::B), col.blue());
418 writeGraphMLAttribute(clusterTag, toString(Attribute::ClusterStroke), CA.strokeColor(c).toString().c_str());
419
420 if(CA.templateCluster(c).length() > 0) {
421 writeGraphMLAttribute(clusterTag, toString(Attribute::Template), CA.templateCluster(c).c_str());
422 }
423
424 // TODO: not important cluster attributes like fill patterns, background
425 // color, stroke width, etc.
426 }
427
428
writeGraphML(const Graph & G,std::ostream & out)429 bool GraphIO::writeGraphML(const Graph &G, std::ostream &out)
430 {
431 bool result = out.good();
432
433 if(result) {
434 pugi::xml_document doc;
435
436 pugi::xml_node rootNode = writeGraphMLHeader(doc);
437 pugi::xml_node graphNode = writeGraphTag(rootNode, "directed");
438
439 for (node v : G.nodes) {
440 writeGraphMLNode(graphNode, v);
441 }
442
443 for (edge e : G.edges) {
444 writeGraphMLEdge(graphNode, e);
445 }
446
447 doc.save(out);
448 }
449
450 return result;
451 }
452
453
writeGraphML(const ClusterGraph & C,std::ostream & out)454 bool GraphIO::writeGraphML(const ClusterGraph &C, std::ostream &out)
455 {
456 bool result = out.good();
457
458 if(result) {
459 const Graph &G = C.constGraph();
460 pugi::xml_document doc;
461
462 pugi::xml_node rootNode = writeGraphMLHeader(doc);
463 pugi::xml_node graphNode = writeGraphTag(rootNode, "directed");
464
465 writeGraphMLCluster(graphNode, G, C.rootCluster(), 0);
466
467 for (edge e : G.edges) {
468 writeGraphMLEdge(graphNode, e);
469 }
470
471 doc.save(out);
472 }
473
474 return result;
475 }
476
477
writeGraphML(const GraphAttributes & GA,std::ostream & out)478 bool GraphIO::writeGraphML(const GraphAttributes &GA, std::ostream &out)
479 {
480 bool result = out.good();
481
482 if(result) {
483 const Graph &G = GA.constGraph();
484 const std::string edgeDefault = GA.directed() ? "directed" : "undirected";
485 pugi::xml_document doc;
486
487 pugi::xml_node rootNode = writeGraphMLHeader(doc);
488 defineGraphMLAttributes(rootNode, GA.attributes());
489 pugi::xml_node graphNode = writeGraphTag(rootNode, edgeDefault);
490
491 for (node v : G.nodes) {
492 writeGraphMLNode(graphNode, GA, v);
493 }
494
495 for (edge e : G.edges) {
496 writeGraphMLEdge(graphNode, GA, e);
497 }
498
499 doc.save(out);
500 }
501
502 return result;
503 }
504
505
writeGraphML(const ClusterGraphAttributes & CA,std::ostream & out)506 bool GraphIO::writeGraphML(const ClusterGraphAttributes &CA, std::ostream &out)
507 {
508 bool result = out.good();
509
510 if(result) {
511 const Graph &G = CA.constGraph();
512 const ClusterGraph &C = CA.constClusterGraph();
513 pugi::xml_document doc;
514
515 pugi::xml_node rootNode = writeGraphMLHeader(doc);
516 defineGraphMLAttributes(rootNode, CA.attributes());
517 defineGraphMLAttribute(rootNode, "node", toString(graphml::Attribute::ClusterStroke), "string");
518 pugi::xml_node graphNode = writeGraphTag(rootNode, "directed");
519 writeGraphMLCluster(graphNode, CA, C.rootCluster(), 0);
520
521 for (edge e : G.edges) {
522 writeGraphMLEdge(graphNode, CA, e);
523 }
524
525 doc.save(out);
526 }
527
528 return result;
529 }
530
531
532 }
533