001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.stream.file; 033 034import org.graphstream.graph.Edge; 035import org.graphstream.graph.Element; 036import org.graphstream.graph.Graph; 037import org.graphstream.graph.Node; 038import org.graphstream.util.cumulative.CumulativeAttributes; 039import org.graphstream.util.cumulative.CumulativeSpells; 040import org.graphstream.util.cumulative.CumulativeSpells.Spell; 041import org.graphstream.util.cumulative.GraphSpells; 042 043import java.io.IOException; 044import java.net.URI; 045import java.net.URL; 046import java.text.DateFormat; 047import java.text.DecimalFormat; 048import java.text.DecimalFormatSymbols; 049import java.text.Format; 050import java.text.SimpleDateFormat; 051import java.util.Calendar; 052import java.util.Collection; 053import java.util.Date; 054import java.util.HashMap; 055import java.util.Locale; 056 057import javax.xml.stream.FactoryConfigurationError; 058import javax.xml.stream.XMLOutputFactory; 059import javax.xml.stream.XMLStreamException; 060import javax.xml.stream.XMLStreamWriter; 061 062public class FileSinkGEXF extends FileSinkBase { 063 public static enum TimeFormat { 064 INTEGER(new DecimalFormat("#", new DecimalFormatSymbols(Locale.ROOT))), DOUBLE( 065 new DecimalFormat("#.0###################", 066 new DecimalFormatSymbols(Locale.ROOT))), DATE( 067 new SimpleDateFormat("yyyy-MM-dd")), DATETIME( 068 new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSZ")); 069 Format format; 070 071 TimeFormat(Format f) { 072 this.format = f; 073 } 074 } 075 076 XMLStreamWriter stream; 077 boolean smart; 078 int depth; 079 int currentAttributeIndex = 0; 080 GraphSpells graphSpells; 081 TimeFormat timeFormat; 082 083 public FileSinkGEXF() { 084 smart = true; 085 depth = 0; 086 graphSpells = null; 087 timeFormat = TimeFormat.DOUBLE; 088 } 089 090 public void setTimeFormat(TimeFormat format) { 091 this.timeFormat = format; 092 } 093 094 protected void putSpellAttributes(Spell s) throws XMLStreamException { 095 if (s.isStarted()) { 096 String start = s.isStartOpen() ? "startopen" : "start"; 097 String date = timeFormat.format.format(s.getStartDate()); 098 099 stream.writeAttribute(start, date); 100 } 101 102 if (s.isEnded()) { 103 String end = s.isEndOpen() ? "endopen" : "end"; 104 String date = timeFormat.format.format(s.getEndDate()); 105 106 stream.writeAttribute(end, date); 107 } 108 } 109 110 protected void outputEndOfFile() throws IOException { 111 try { 112 if (graphSpells != null) { 113 exportGraphSpells(); 114 graphSpells = null; 115 } 116 117 endElement(stream, false); 118 stream.writeEndDocument(); 119 stream.flush(); 120 } catch (XMLStreamException e) { 121 throw new IOException(e); 122 } 123 } 124 125 protected void outputHeader() throws IOException { 126 Calendar cal = Calendar.getInstance(); 127 Date date = cal.getTime(); 128 DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, 129 DateFormat.SHORT); 130 131 try { 132 stream = XMLOutputFactory.newFactory() 133 .createXMLStreamWriter(output); 134 stream.writeStartDocument("UTF-8", "1.0"); 135 136 startElement(stream, "gexf"); 137 stream.writeAttribute("xmlns", "http://www.gexf.net/1.2draft"); 138 stream.writeAttribute("xmlns:xsi", 139 "http://www.w3.org/2001/XMLSchema-instance"); 140 stream.writeAttribute("xsi:schemaLocation", 141 "http://www.gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd"); 142 stream.writeAttribute("version", "1.2"); 143 144 startElement(stream, "meta"); 145 stream.writeAttribute("lastmodifieddate", df.format(date)); 146 startElement(stream, "creator"); 147 stream.writeCharacters("GraphStream - " + getClass().getName()); 148 endElement(stream, true); 149 endElement(stream, false); 150 } catch (XMLStreamException e) { 151 throw new IOException(e); 152 } catch (FactoryConfigurationError e) { 153 throw new IOException(e); 154 } 155 } 156 157 protected void startElement(XMLStreamWriter stream, String name) 158 throws XMLStreamException { 159 if (smart) { 160 stream.writeCharacters("\n"); 161 162 for (int i = 0; i < depth; i++) 163 stream.writeCharacters(" "); 164 } 165 166 stream.writeStartElement(name); 167 depth++; 168 } 169 170 protected void endElement(XMLStreamWriter stream, boolean leaf) 171 throws XMLStreamException { 172 depth--; 173 174 if (smart && !leaf) { 175 stream.writeCharacters("\n"); 176 177 for (int i = 0; i < depth; i++) 178 stream.writeCharacters(" "); 179 } 180 181 stream.writeEndElement(); 182 } 183 184 @Override 185 protected void exportGraph(Graph g) { 186 GEXFAttributeMap nodeAttributes = new GEXFAttributeMap("node", g); 187 GEXFAttributeMap edgeAttributes = new GEXFAttributeMap("edge", g); 188 189 try { 190 startElement(stream, "graph"); 191 stream.writeAttribute("defaultedgetype", "undirected"); 192 193 nodeAttributes.export(stream); 194 edgeAttributes.export(stream); 195 196 startElement(stream, "nodes"); 197 for (Node n : g.getEachNode()) { 198 startElement(stream, "node"); 199 stream.writeAttribute("id", n.getId()); 200 201 if (n.hasAttribute("label")) 202 stream.writeAttribute("label", n.getAttribute("label") 203 .toString()); 204 205 if (n.getAttributeCount() > 0) { 206 startElement(stream, "attvalues"); 207 for (String key : n.getAttributeKeySet()) 208 nodeAttributes.push(stream, n, key); 209 endElement(stream, false); 210 } 211 212 endElement(stream, n.getAttributeCount() == 0); 213 } 214 endElement(stream, false); 215 216 startElement(stream, "edges"); 217 for (Edge e : g.getEachEdge()) { 218 startElement(stream, "edge"); 219 220 stream.writeAttribute("id", e.getId()); 221 stream.writeAttribute("source", e.getSourceNode().getId()); 222 stream.writeAttribute("target", e.getTargetNode().getId()); 223 224 if (e.getAttributeCount() > 0) { 225 startElement(stream, "attvalues"); 226 for (String key : e.getAttributeKeySet()) 227 edgeAttributes.push(stream, e, key); 228 endElement(stream, false); 229 } 230 231 endElement(stream, e.getAttributeCount() == 0); 232 } 233 endElement(stream, false); 234 235 endElement(stream, false); 236 } catch (XMLStreamException e1) { 237 e1.printStackTrace(); 238 } 239 } 240 241 protected void exportGraphSpells() { 242 GEXFAttributeMap nodeAttributes = new GEXFAttributeMap("node", 243 graphSpells); 244 GEXFAttributeMap edgeAttributes = new GEXFAttributeMap("edge", 245 graphSpells); 246 247 try { 248 startElement(stream, "graph"); 249 stream.writeAttribute("mode", "dynamic"); 250 stream.writeAttribute("defaultedgetype", "undirected"); 251 stream.writeAttribute("timeformat", timeFormat.name().toLowerCase()); 252 253 nodeAttributes.export(stream); 254 edgeAttributes.export(stream); 255 256 startElement(stream, "nodes"); 257 for (String nodeId : graphSpells.getNodes()) { 258 startElement(stream, "node"); 259 stream.writeAttribute("id", nodeId); 260 261 CumulativeAttributes attr = graphSpells 262 .getNodeAttributes(nodeId); 263 Object label = attr.getAny("label"); 264 265 if (label != null) 266 stream.writeAttribute("label", label.toString()); 267 268 CumulativeSpells spells = graphSpells.getNodeSpells(nodeId); 269 270 if (!spells.isEternal()) { 271 startElement(stream, "spells"); 272 for (int i = 0; i < spells.getSpellCount(); i++) { 273 Spell s = spells.getSpell(i); 274 275 startElement(stream, "spell"); 276 putSpellAttributes(s); 277 endElement(stream, true); 278 } 279 endElement(stream, false); 280 } 281 282 if (attr.getAttributesCount() > 0) { 283 startElement(stream, "attvalues"); 284 nodeAttributes.push(stream, nodeId, graphSpells); 285 endElement(stream, false); 286 } 287 288 endElement(stream, 289 spells.isEternal() && attr.getAttributesCount() == 0); 290 } 291 endElement(stream, false); 292 293 startElement(stream, "edges"); 294 for (String edgeId : graphSpells.getEdges()) { 295 startElement(stream, "edge"); 296 297 GraphSpells.EdgeData data = graphSpells.getEdgeData(edgeId); 298 299 stream.writeAttribute("id", edgeId); 300 stream.writeAttribute("source", data.getSource()); 301 stream.writeAttribute("target", data.getTarget()); 302 303 CumulativeAttributes attr = graphSpells 304 .getEdgeAttributes(edgeId); 305 306 CumulativeSpells spells = graphSpells.getEdgeSpells(edgeId); 307 308 if (!spells.isEternal()) { 309 startElement(stream, "spells"); 310 for (int i = 0; i < spells.getSpellCount(); i++) { 311 Spell s = spells.getSpell(i); 312 313 startElement(stream, "spell"); 314 putSpellAttributes(s); 315 endElement(stream, true); 316 } 317 endElement(stream, false); 318 } 319 320 if (attr.getAttributesCount() > 0) { 321 startElement(stream, "attvalues"); 322 edgeAttributes.push(stream, edgeId, graphSpells); 323 endElement(stream, false); 324 } 325 326 endElement(stream, 327 spells.isEternal() && attr.getAttributesCount() == 0); 328 } 329 endElement(stream, false); 330 331 endElement(stream, false); 332 } catch (XMLStreamException e1) { 333 e1.printStackTrace(); 334 } 335 } 336 337 protected void checkGraphSpells() { 338 if (graphSpells == null) 339 graphSpells = new GraphSpells(); 340 } 341 342 public void edgeAttributeAdded(String sourceId, long timeId, String edgeId, 343 String attribute, Object value) { 344 checkGraphSpells(); 345 graphSpells.edgeAttributeAdded(sourceId, timeId, edgeId, attribute, 346 value); 347 } 348 349 public void edgeAttributeChanged(String sourceId, long timeId, 350 String edgeId, String attribute, Object oldValue, Object newValue) { 351 checkGraphSpells(); 352 graphSpells.edgeAttributeChanged(sourceId, timeId, edgeId, attribute, 353 oldValue, newValue); 354 } 355 356 public void edgeAttributeRemoved(String sourceId, long timeId, 357 String edgeId, String attribute) { 358 checkGraphSpells(); 359 graphSpells.edgeAttributeRemoved(sourceId, timeId, edgeId, attribute); 360 } 361 362 public void graphAttributeAdded(String sourceId, long timeId, 363 String attribute, Object value) { 364 checkGraphSpells(); 365 graphSpells.graphAttributeAdded(sourceId, timeId, attribute, value); 366 } 367 368 public void graphAttributeChanged(String sourceId, long timeId, 369 String attribute, Object oldValue, Object newValue) { 370 checkGraphSpells(); 371 graphSpells.graphAttributeChanged(sourceId, timeId, attribute, 372 oldValue, newValue); 373 } 374 375 public void graphAttributeRemoved(String sourceId, long timeId, 376 String attribute) { 377 checkGraphSpells(); 378 graphSpells.graphAttributeRemoved(sourceId, timeId, attribute); 379 } 380 381 public void nodeAttributeAdded(String sourceId, long timeId, String nodeId, 382 String attribute, Object value) { 383 checkGraphSpells(); 384 graphSpells.nodeAttributeAdded(sourceId, timeId, nodeId, attribute, 385 value); 386 } 387 388 public void nodeAttributeChanged(String sourceId, long timeId, 389 String nodeId, String attribute, Object oldValue, Object newValue) { 390 checkGraphSpells(); 391 graphSpells.nodeAttributeChanged(sourceId, timeId, nodeId, attribute, 392 oldValue, newValue); 393 } 394 395 public void nodeAttributeRemoved(String sourceId, long timeId, 396 String nodeId, String attribute) { 397 checkGraphSpells(); 398 graphSpells.nodeAttributeRemoved(sourceId, timeId, nodeId, attribute); 399 } 400 401 public void edgeAdded(String sourceId, long timeId, String edgeId, 402 String fromNodeId, String toNodeId, boolean directed) { 403 checkGraphSpells(); 404 graphSpells.edgeAdded(sourceId, timeId, edgeId, fromNodeId, toNodeId, 405 directed); 406 } 407 408 public void edgeRemoved(String sourceId, long timeId, String edgeId) { 409 checkGraphSpells(); 410 graphSpells.edgeRemoved(sourceId, timeId, edgeId); 411 } 412 413 public void graphCleared(String sourceId, long timeId) { 414 checkGraphSpells(); 415 graphSpells.graphCleared(sourceId, timeId); 416 } 417 418 public void nodeAdded(String sourceId, long timeId, String nodeId) { 419 checkGraphSpells(); 420 graphSpells.nodeAdded(sourceId, timeId, nodeId); 421 } 422 423 public void nodeRemoved(String sourceId, long timeId, String nodeId) { 424 checkGraphSpells(); 425 graphSpells.nodeRemoved(sourceId, timeId, nodeId); 426 } 427 428 public void stepBegins(String sourceId, long timeId, double step) { 429 checkGraphSpells(); 430 graphSpells.stepBegins(sourceId, timeId, step); 431 } 432 433 class GEXFAttribute { 434 int index; 435 String key; 436 String type; 437 438 GEXFAttribute(String key, String type) { 439 this.index = currentAttributeIndex++; 440 this.key = key; 441 this.type = type; 442 } 443 } 444 445 class GEXFAttributeMap extends HashMap<String, GEXFAttribute> { 446 private static final long serialVersionUID = 6176508111522815024L; 447 protected String type; 448 449 GEXFAttributeMap(String type, Graph g) { 450 this.type = type; 451 452 Iterable<? extends Element> iterable; 453 454 if (type.equals("node")) 455 iterable = (Iterable<? extends Element>) g.getNodeSet(); 456 else 457 iterable = (Iterable<? extends Element>) g.getEdgeSet(); 458 459 for (Element e : iterable) { 460 for (String key : e.getAttributeKeySet()) { 461 Object value = e.getAttribute(key); 462 check(key, value); 463 } 464 } 465 } 466 467 GEXFAttributeMap(String type, GraphSpells spells) { 468 this.type = type; 469 470 if (type.equals("node")) { 471 for (String nodeId : spells.getNodes()) { 472 CumulativeAttributes attr = spells 473 .getNodeAttributes(nodeId); 474 475 for (String key : attr.getAttributes()) { 476 for (Spell s : attr.getAttributeSpells(key)) { 477 Object value = s.getAttachedData(); 478 check(key, value); 479 } 480 } 481 } 482 } else { 483 for (String edgeId : spells.getEdges()) { 484 CumulativeAttributes attr = spells 485 .getEdgeAttributes(edgeId); 486 487 for (String key : attr.getAttributes()) { 488 for (Spell s : attr.getAttributeSpells(key)) { 489 Object value = s.getAttachedData(); 490 check(key, value); 491 } 492 } 493 } 494 } 495 } 496 497 void check(String key, Object value) { 498 String id = getID(key, value); 499 String attType = "string"; 500 501 if (containsKey(id)) 502 return; 503 504 if (value instanceof Integer || value instanceof Short) 505 attType = "integer"; 506 else if (value instanceof Long) 507 attType = "long"; 508 else if (value instanceof Float) 509 attType = "float"; 510 else if (value instanceof Double) 511 attType = "double"; 512 else if (value instanceof Boolean) 513 attType = "boolean"; 514 else if (value instanceof URL || value instanceof URI) 515 attType = "anyURI"; 516 else if (value.getClass().isArray() || value instanceof Collection) 517 attType = "liststring"; 518 519 put(id, new GEXFAttribute(key, attType)); 520 } 521 522 String getID(String key, Object value) { 523 return String.format("%s@%s", key, value.getClass().getName()); 524 } 525 526 void export(XMLStreamWriter stream) throws XMLStreamException { 527 if (size() == 0) 528 return; 529 530 startElement(stream, "attributes"); 531 stream.writeAttribute("class", type); 532 533 for (GEXFAttribute a : values()) { 534 startElement(stream, "attribute"); 535 stream.writeAttribute("id", Integer.toString(a.index)); 536 stream.writeAttribute("title", a.key); 537 stream.writeAttribute("type", a.type); 538 endElement(stream, true); 539 } 540 541 endElement(stream, size() == 0); 542 } 543 544 void push(XMLStreamWriter stream, Element e, String key) 545 throws XMLStreamException { 546 String id = getID(key, e.getAttribute(key)); 547 GEXFAttribute a = get(id); 548 549 if (a == null) { 550 // TODO 551 return; 552 } 553 554 startElement(stream, "attvalue"); 555 stream.writeAttribute("for", Integer.toString(a.index)); 556 stream.writeAttribute("value", e.getAttribute(key).toString()); 557 endElement(stream, true); 558 } 559 560 void push(XMLStreamWriter stream, String elementId, GraphSpells spells) 561 throws XMLStreamException { 562 CumulativeAttributes attr; 563 564 if (type.equals("node")) 565 attr = spells.getNodeAttributes(elementId); 566 else 567 attr = spells.getEdgeAttributes(elementId); 568 569 for (String key : attr.getAttributes()) { 570 for (Spell s : attr.getAttributeSpells(key)) { 571 Object value = s.getAttachedData(); 572 String id = getID(key, value); 573 GEXFAttribute a = get(id); 574 575 if (a == null) { 576 // TODO 577 return; 578 } 579 580 startElement(stream, "attvalue"); 581 stream.writeAttribute("for", Integer.toString(a.index)); 582 stream.writeAttribute("value", value.toString()); 583 putSpellAttributes(s); 584 endElement(stream, true); 585 } 586 } 587 } 588 } 589}