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