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.gml; 033 034import java.io.IOException; 035import java.util.HashMap; 036 037import org.graphstream.graph.implementations.AbstractElement.AttributeChangeEvent; 038import org.graphstream.stream.SourceBase.ElementType; 039import org.graphstream.stream.file.FileSourceGML; 040 041public class GMLContext { 042 FileSourceGML gml; 043 //GMLParser parser; 044 String sourceId; 045 boolean directed; 046 protected KeyValues nextStep = null; 047 boolean inGraph = false; 048 049 GMLContext(FileSourceGML gml) { 050 this.gml = gml; 051 this.sourceId = String.format("<GML stream %d>", 052 System.currentTimeMillis()); 053 } 054 055 void handleKeyValues(KeyValues kv) throws IOException { 056 if (nextStep != null) { 057 insertKeyValues(nextStep); 058 nextStep = null; 059 } 060 061 try { 062 if (kv != null) { 063 insertKeyValues(kv); 064 } 065 } catch (IOException e) { 066 throw new IOException(e); 067 } 068 } 069 070 void setNextStep(KeyValues kv) { 071 nextStep = kv; 072 } 073 074 public void setDirected(boolean on) { 075 directed = on; 076 } 077 078 void setIsInGraph(boolean on) { 079 inGraph = on; 080 } 081 082 public void addNodeOrEdge(String element, KeyValues kv) { 083 System.err.printf("adding %s %n", element); 084 } 085 086 protected void insertKeyValues(KeyValues kv) throws IOException { 087 if (kv.key != null) { 088 if (inGraph) { 089 if (kv.key.equals("node") || kv.key.equals("add-node")) { 090 handleAddNode(kv); 091 } else if (kv.key.equals("edge") || kv.key.equals("add-edge")) { 092 handleAddEdge(kv); 093 } else if (kv.key.equals("del-node") || kv.key.equals("-node")) { 094 handleDelNode(kv); 095 } else if (kv.key.equals("del-edge") || kv.key.equals("-edge")) { 096 handleDelEdge(kv); 097 } else if (kv.key.equals("change-node") 098 || kv.key.equals("+node")) { 099 handleChangeNode(kv); 100 } else if (kv.key.equals("change-edge") 101 || kv.key.equals("+edge")) { 102 handleChangeEdge(kv); 103 } else if (kv.key.equals("step")) { 104 handleStep(kv); 105 } else if (kv.key.equals("directed")) { 106 setDirected(getBoolean(kv.get("directed"))); 107 } else { 108 if (kv.key.startsWith("-")) { 109 gml.sendAttributeChangedEvent(sourceId, sourceId, 110 ElementType.GRAPH, kv.key.substring(1), 111 AttributeChangeEvent.REMOVE, null, null); 112 } else { 113 gml.sendAttributeChangedEvent(sourceId, sourceId, 114 ElementType.GRAPH, kv.key, 115 AttributeChangeEvent.ADD, null, 116 compositeAttribute(kv)); 117 } 118 } 119 } else { 120 // XXX Should we consider these events pertain to the graph ? 121 // XXX 122 123 if (kv.key.startsWith("-")) { 124 gml.sendAttributeChangedEvent(sourceId, sourceId, 125 ElementType.GRAPH, kv.key.substring(1), 126 AttributeChangeEvent.REMOVE, null, null); 127 } else { 128 gml.sendAttributeChangedEvent(sourceId, sourceId, 129 ElementType.GRAPH, kv.key, 130 AttributeChangeEvent.ADD, null, 131 compositeAttribute(kv)); 132 } 133 } 134 } 135 } 136 137 protected Object compositeAttribute(KeyValues kv) { 138 if (kv.size() < 2) { 139 return kv.get(kv.key); 140 } else { 141 return kv; 142 } 143 } 144 145 protected void handleAddNode(KeyValues kv) throws IOException { 146 Object thing = kv.get("node"); 147 if (thing == null) 148 thing = kv.get("add-node"); 149 if (thing == null) 150 kv.error("expecting a node or add-node token here"); 151 152 if (thing instanceof String) { 153 String id = (String) thing; 154 gml.sendNodeAdded(sourceId, id); 155 } else if (thing instanceof KeyValues) { 156 KeyValues node = (KeyValues) thing; 157 String id = node.reqStringOrNumber("id"); 158 159 gml.sendNodeAdded(sourceId, id); 160 handleNodeAttributes(id, node); 161 } else { 162 kv.error("unknown token type"); 163 } 164 } 165 166 protected long edgeid = 0; 167 168 protected void handleAddEdge(KeyValues kv) throws IOException { 169 Object thing = kv.get("edge"); 170 if (thing == null) 171 thing = kv.get("add-edge"); 172 if (thing == null) 173 kv.error("expecting a edge or add-edge token here"); 174 if (!(thing instanceof KeyValues)) 175 kv.error("expecting a set of values for the new edge"); 176 177 KeyValues edge = (KeyValues) thing; 178 String id = edge.optString("id"); 179 180 String src = edge.reqStringOrNumber("source"); 181 String trg = edge.reqStringOrNumber("target"); 182 183 if (id == null) 184 id = String.format("%s_%s_%d", src, trg, edgeid++); 185 186 String dir = edge.optString("directed"); 187 188 boolean directed = this.directed; 189 190 if (dir != null) { 191 directed = getBoolean(dir); 192 } 193 194 gml.sendEdgeAdded(sourceId, id, src, trg, directed); 195 196 handleEdgeAttributes(id, edge); 197 } 198 199 protected void handleDelNode(KeyValues kv) throws IOException { 200 Object thing = kv.get("del-node"); 201 if (thing == null) 202 thing = kv.get("-node"); 203 if (thing == null) 204 kv.error("expecting a del-node or -node token here"); 205 206 if (thing instanceof String) { 207 String id = (String) thing; 208 gml.sendNodeRemoved(sourceId, id); 209 } else if (thing instanceof KeyValues) { 210 KeyValues node = (KeyValues) thing; 211 String id = node.reqString("id"); 212 gml.sendNodeRemoved(sourceId, id); 213 } else { 214 kv.error("unknown token type"); 215 } 216 } 217 218 protected void handleDelEdge(KeyValues kv) throws IOException { 219 Object thing = kv.get("del-edge"); 220 if (thing == null) 221 thing = kv.get("-edge"); 222 if (thing == null) 223 kv.error("expecting a del-edge or -edge token here"); 224 225 if (thing instanceof String) { 226 String id = (String) thing; 227 gml.sendEdgeRemoved(sourceId, id); 228 } else if (thing instanceof KeyValues) { 229 KeyValues edge = (KeyValues) thing; 230 String id = edge.reqString("id"); 231 gml.sendEdgeRemoved(sourceId, id); 232 } else { 233 kv.error("unknown token type"); 234 } 235 } 236 237 protected void handleChangeNode(KeyValues kv) throws IOException { 238 Object thing = kv.get("change-node"); 239 if (thing == null) 240 thing = kv.get("+node"); 241 if (thing == null) 242 kv.error("expecting a change-node or +node token here"); 243 if (!(thing instanceof KeyValues)) 244 kv.error("expecting a set of values"); 245 246 KeyValues node = (KeyValues) thing; 247 String id = node.reqString("id"); 248 249 handleNodeAttributes(id, node); 250 } 251 252 protected void handleChangeEdge(KeyValues kv) throws IOException { 253 Object thing = kv.get("change-edge"); 254 if (thing == null) 255 thing = kv.get("+edge"); 256 if (thing == null) 257 kv.error("expecting a change-edge or +edge token here"); 258 if (!(thing instanceof KeyValues)) 259 kv.error("expecting a set of values"); 260 261 KeyValues edge = (KeyValues) thing; 262 String id = edge.reqString("id"); 263 264 handleEdgeAttributes(id, edge); 265 } 266 267 protected void handleNodeAttributes(String id, KeyValues node) { 268 for (String key : node.keySet()) { 269 if (key.startsWith("-")) { 270 if (key.equals("-label")) 271 key = "-ui.label"; 272 273 gml.sendAttributeChangedEvent(sourceId, id, ElementType.NODE, 274 key.substring(1), AttributeChangeEvent.REMOVE, null, 275 null); 276 } else { 277 if (key.equals("graphics") 278 && node.get("graphics") instanceof KeyValues) { 279 Graphics graphics = optNodeStyle((KeyValues) node 280 .get("graphics")); 281 282 if (graphics != null) { 283 if (graphics.position != null) { 284 gml.sendAttributeChangedEvent(sourceId, id, 285 ElementType.NODE, "xyz", 286 AttributeChangeEvent.ADD, null, 287 graphics.getPosition()); 288 } 289 if (graphics.style != null) { 290 gml.sendAttributeChangedEvent(sourceId, id, 291 ElementType.NODE, "ui.style", 292 AttributeChangeEvent.ADD, null, 293 graphics.style); 294 } 295 } 296 } else { 297 String k = key; 298 299 if (key.equals("label")) 300 k = "ui.label"; 301 302 gml.sendAttributeChangedEvent(sourceId, id, 303 ElementType.NODE, k, AttributeChangeEvent.ADD, 304 null, node.get(key)); 305 } 306 } 307 } 308 } 309 310 protected void handleEdgeAttributes(String id, KeyValues edge) { 311 for (String key : edge.keySet()) { 312 if (key.startsWith("-")) { 313 if (key.equals("-label")) 314 key = "-ui.label"; 315 316 gml.sendAttributeChangedEvent(sourceId, id, ElementType.EDGE, 317 key.substring(1), AttributeChangeEvent.REMOVE, null, 318 null); 319 } else { 320 if (key.equals("graphics") 321 && edge.get("graphics") instanceof KeyValues) { 322 Graphics graphics = optEdgeStyle((KeyValues) edge 323 .get("graphics")); 324 325 if (graphics != null) { 326 if (graphics.style != null) { 327 gml.sendAttributeChangedEvent(sourceId, id, 328 ElementType.EDGE, "ui.style", 329 AttributeChangeEvent.ADD, null, 330 graphics.style); 331 } 332 } 333 } else { 334 String k = key; 335 336 if (key.equals("label")) 337 k = "ui.label"; 338 339 gml.sendAttributeChangedEvent(sourceId, id, 340 ElementType.EDGE, k, AttributeChangeEvent.ADD, 341 null, edge.get(key)); 342 } 343 } 344 } 345 } 346 347 protected void handleStep(KeyValues kv) throws IOException { 348 gml.sendStepBegins(sourceId, kv.reqNumber("step")); 349 } 350 351 protected Graphics optNodeStyle(KeyValues kv) { 352 Graphics graphics = null; 353 354 if (kv != null) { 355 StringBuffer style = new StringBuffer(); 356 String w = null, h = null, d = null; 357 graphics = new Graphics(); 358 359 if (kv.get("x") != null) { 360 graphics.setX(asDouble((String) kv.get("x"))); 361 } 362 if (kv.get("y") != null) { 363 graphics.setY(asDouble((String) kv.get("y"))); 364 } 365 if (kv.get("z") != null) { 366 graphics.setZ(asDouble((String) kv.get("z"))); 367 } 368 if (kv.get("w") != null) { 369 w = (String) kv.get("w"); 370 } 371 if (kv.get("h") != null) { 372 h = (String) kv.get("h"); 373 } 374 if (kv.get("d") != null) { 375 d = (String) kv.get("d"); 376 } 377 if (w != null || h != null || d != null) { 378 int ww = w != null ? (int) asDouble(w) : 0; 379 int hh = h != null ? (int) asDouble(h) : 0; 380 int dd = d != null ? (int) asDouble(d) : 0; 381 style.append(String.format("size: %dpx, %dpx, %dpx; ", ww, hh, 382 dd)); 383 } 384 if (kv.get("type") != null) { 385 style.append(String.format("shape: %s; ", 386 asNodeShape((String) kv.get("type")))); 387 } 388 389 commonGraphicsAttributes(kv, style); 390 graphics.style = style.toString(); 391 } 392 393 return graphics; 394 } 395 396 protected Graphics optEdgeStyle(KeyValues kv) { 397 Graphics graphics = null; 398 399 if (kv != null) { 400 StringBuffer style = new StringBuffer(); 401 String w = null; 402 graphics = new Graphics(); 403 404 if (kv.get("width") != null) { 405 w = (String) kv.get("width"); 406 } else if (kv.get("w") != null) { 407 w = (String) kv.get("w"); 408 } 409 if (w != null) { 410 double ww = w != null ? asDouble(w) : 0.0; 411 style.append(String.format("size: %fpx;", ww)); 412 } 413 if (kv.get("type") != null) { 414 style.append(String.format("shape: %s; ", 415 asEdgeShape((String) kv.get("type")))); 416 } 417 418 commonGraphicsAttributes(kv, style); 419 graphics.style = style.toString(); 420 } 421 422 return graphics; 423 } 424 425 protected void commonGraphicsAttributes(KeyValues kv, StringBuffer style) { 426 if (kv.get("fill") != null) { 427 style.append(String.format("fill-color: %s; ", kv.get("fill"))); 428 } 429 if (kv.get("outline") != null) { 430 style.append(String.format("stroke-color: %s; ", kv.get("outline"))); 431 } 432 if (kv.get("outline_width") != null) { 433 style.append(String.format("stroke-width: %spx; ", 434 kv.get("outline_width"))); 435 } 436 if ((kv.get("outline") != null) || (kv.get("outline_width") != null)) { 437 style.append("stroke-mode: plain; "); 438 } 439 if (kv.get("anchor") != null) { 440 style.append(String.format("text-alginment: %s; ", 441 asTextAlignment((String) kv.get("anchor")))); 442 } 443 if (kv.get("image") != null) { 444 style.append(String.format("icon-mode: at-left; icon: %s; ", 445 (String) kv.get("image"))); 446 } 447 if (kv.get("arrow") != null) { 448 style.append(String.format("arrow-shape: %s; ", 449 asArrowShape((String) kv.get("arrow")))); 450 } 451 if (kv.get("font") != null) { 452 style.append(String.format("font: %s; ", (String) kv.get("font"))); 453 } 454 } 455 456 protected double asDouble(String value) { 457 try { 458 return Double.parseDouble(value); 459 } catch (NumberFormatException e) { 460 return 0.0; 461 } 462 } 463 464 protected String asNodeShape(String type) { 465 if (type.equals("ellipse") || type.equals("oval")) { 466 return "circle"; 467 } else if (type.equals("rectangle") || type.equals("box")) { 468 return "box"; 469 } else if (type.equals("rounded-box")) { 470 return "rounded-box"; 471 } else if (type.equals("cross")) { 472 return "cross"; 473 } else if (type.equals("freeplane")) { 474 return "freeplane"; 475 } else if (type.equals("losange") || type.equals("diamond")) { 476 return "diamond"; 477 } else { 478 return "circle"; 479 } 480 } 481 482 protected String asEdgeShape(String type) { 483 if (type.equals("line")) { 484 return "line"; 485 } else if (type.equals("cubic-curve")) { 486 return "cubic-curve"; 487 } else if (type.equals("angle")) { 488 return "angle"; 489 } else if (type.equals("blob")) { 490 return "blob"; 491 } else if (type.equals("freeplane")) { 492 return "freeplane"; 493 } else { 494 return "line"; 495 } 496 } 497 498 protected String asTextAlignment(String anchor) { 499 if (anchor.equals("c")) { 500 return "center"; 501 } else if (anchor.equals("n")) { 502 return "above"; 503 } else if (anchor.equals("ne")) { 504 return "at-right"; 505 } else if (anchor.equals("e")) { 506 return "at-right"; 507 } else if (anchor.equals("se")) { 508 return "at-right"; 509 } else if (anchor.equals("s")) { 510 return "under"; 511 } else if (anchor.equals("sw")) { 512 return "at-left"; 513 } else if (anchor.equals("w")) { 514 return "at-left"; 515 } else if (anchor.equals("nw")) { 516 return "at-left"; 517 } else { 518 return "center"; 519 } 520 } 521 522 protected String asArrowShape(String arrow) { 523 if (arrow.equals("none")) { 524 return "none"; 525 } else if (arrow.equals("last")) { 526 return "arrow"; 527 } else { 528 return "none"; 529 } 530 } 531 532 protected boolean getBoolean(Object bool) { 533 if(bool instanceof String) { 534 return (bool.equals("1") || bool.equals("true") || bool.equals("yes") 535 || bool.equals("y")); 536 } else if(bool instanceof Number) { 537 return (((Number)bool).doubleValue() != 0); 538 } 539 return false; 540 } 541} 542 543class Graphics { 544 public double[] position = null; 545 public String style = null; 546 547 public void setX(double value) { 548 if (position == null) 549 position = new double[3]; 550 551 position[0] = value; 552 } 553 554 public void setY(double value) { 555 if (position == null) 556 position = new double[3]; 557 558 position[1] = value; 559 } 560 561 public void setZ(double value) { 562 if (position == null) 563 position = new double[3]; 564 565 position[2] = value; 566 } 567 568 public Object[] getPosition() { 569 Object p[] = new Object[3]; 570 p[0] = (Double) position[0]; 571 p[1] = (Double) position[1]; 572 p[2] = (Double) position[2]; 573 return p; 574 } 575} 576 577class KeyValues extends HashMap<String, Object> { 578 private static final long serialVersionUID = 5920553787913520204L; 579 580 public String key; 581 public int line; 582 public int column; 583 584 public void print() { 585 System.err.printf("%s:%n", key); 586 for (String k : keySet()) { 587 System.err.printf(" %s: %s%n", k, get(k)); 588 } 589 } 590 591 public String optString(String key) throws IOException { 592 Object o = get(key); 593 594 if (o == null) 595 return null; 596 597 if (o instanceof Number) 598 o = o.toString(); 599 600 if (!(o instanceof String)) 601 throw new IOException( 602 String.format( 603 "%d:%d: expecting a string or number value for tag %s, got a list of values", 604 line, column, key)); 605 606 remove(key); 607 return (String) o; 608 } 609 610 protected String reqString(String key) throws IOException { 611 Object o = get(key); 612 613 if (o == null) 614 throw new IOException(String.format( 615 "%d:%d: expecting a tag %s but none found", line, column, 616 key)); 617 618 if (!(o instanceof String)) 619 throw new IOException( 620 String.format( 621 "%d:%d: expecting a string or number value for tag %s, got a list of values", 622 line, column, key)); 623 624 remove(key); 625 626 return (String) o; 627 } 628 629 protected String reqStringOrNumber(String key) throws IOException { 630 Object o = get(key); 631 632 if (o == null) 633 throw new IOException(String.format( 634 "%d:%d: expecting a tag %s but none found", line, column, 635 key)); 636 637 if (!(o instanceof String) && !(o instanceof Number)) 638 throw new IOException( 639 String.format( 640 "%d:%d: expecting a string or number value for tag %s, got a list of values", 641 line, column, key)); 642 643 remove(key); 644 645 if(o instanceof Number) { 646 o = o.toString(); 647 } 648 649 return (String) o; 650 } 651 652 protected double reqNumber(String key) throws IOException { 653 Object o = get(key); 654 double v = 0.0; 655 656 if (o == null) 657 throw new IOException(String.format( 658 "%d:%d: expecting a tag %s but none found", line, column, 659 key)); 660 661 if (!(o instanceof String)) 662 throw new IOException( 663 String.format( 664 "%d:%d expecting a string or number value for tag %s, got a list of values", 665 line, column, key)); 666 667 try { 668 remove(key); 669 v = Double.parseDouble((String) o); 670 } catch (NumberFormatException e) { 671 throw new IOException(String.format( 672 "%d:%d: expecting a number value for tag %s, got a string", 673 line, column, key)); 674 } 675 676 return v; 677 } 678 679 protected KeyValues optKeyValues(String key) throws IOException { 680 Object o = get(key); 681 682 if (o == null) 683 return null; 684 685 if (!(o instanceof KeyValues)) 686 throw new IOException( 687 String.format( 688 "%d:%d: expecting a list of values for tag %s, got a string or number", 689 line, column, key)); 690 691 remove(key); 692 693 return (KeyValues) o; 694 } 695 696 protected KeyValues reqKeyValues(String key) throws IOException { 697 Object o = get(key); 698 699 if (o == null) 700 throw new IOException(String.format( 701 "%d:%d: expecting a tag %s but none found", line, column, 702 key)); 703 704 if (!(o instanceof KeyValues)) 705 throw new IOException( 706 String.format( 707 "%d:%d: expecting a list of values for tag %s, got a string or number", 708 line, column, key)); 709 710 remove(key); 711 712 return (KeyValues) o; 713 } 714 715 protected void error(String message) throws IOException { 716 throw new IOException(String.format("%d:%d: %s", line, column, message)); 717 } 718}