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.graph.implementations; 033 034import java.io.IOException; 035import java.util.AbstractCollection; 036import java.util.Collection; 037import java.util.Iterator; 038 039import org.graphstream.graph.Edge; 040import org.graphstream.graph.EdgeFactory; 041import org.graphstream.graph.EdgeRejectedException; 042import org.graphstream.graph.ElementNotFoundException; 043import org.graphstream.graph.Graph; 044import org.graphstream.graph.IdAlreadyInUseException; 045import org.graphstream.graph.Node; 046import org.graphstream.graph.NodeFactory; 047import org.graphstream.stream.AttributeSink; 048import org.graphstream.stream.ElementSink; 049import org.graphstream.stream.GraphParseException; 050import org.graphstream.stream.Replayable; 051import org.graphstream.stream.Sink; 052import org.graphstream.stream.SourceBase; 053import org.graphstream.stream.file.FileSink; 054import org.graphstream.stream.file.FileSinkFactory; 055import org.graphstream.stream.file.FileSource; 056import org.graphstream.stream.file.FileSourceFactory; 057import org.graphstream.ui.layout.Layout; 058import org.graphstream.ui.layout.Layouts; 059import org.graphstream.ui.swingViewer.GraphRenderer; 060import org.graphstream.ui.swingViewer.Viewer; 061import org.graphstream.util.GraphListeners; 062 063/** 064 * <p> 065 * This class provides a basic implementation of 066 * {@link org.graphstream.graph.Graph} interface, to minimize the effort 067 * required to implement this interface. It provides event management 068 * implementing all the methods of {@link org.graphstream.stream.Pipe}. It also 069 * manages strict checking and auto-creation policies, as well as other services 070 * as displaying, reading and writing. 071 * </p> 072 * 073 * <p> 074 * Subclasses have to maintain data structures allowing to efficiently access 075 * graph elements by their id or index and iterating on them. They also have to 076 * maintain coherent indices of the graph elements. When AbstractGraph decides 077 * to add or remove elements, it calls one of the "callbacks" 078 * {@link #addNodeCallback(AbstractNode)}, 079 * {@link #addEdgeCallback(AbstractEdge)}, 080 * {@link #removeNodeCallback(AbstractNode)}, 081 * {@link #removeEdgeCallback(AbstractEdge)}, {@link #clearCallback()}. The role 082 * of these callbacks is to update the data structures and to re-index elements 083 * if necessary. 084 * </p> 085 */ 086public abstract class AbstractGraph extends AbstractElement implements Graph, 087 Replayable { 088 // *** Fields *** 089 090 private boolean strictChecking; 091 private boolean autoCreate; 092 GraphListeners listeners; 093 private NodeFactory<? extends AbstractNode> nodeFactory; 094 private EdgeFactory<? extends AbstractEdge> edgeFactory; 095 096 private double step = 0; 097 098 private boolean nullAttributesAreErrors; 099 100 private long replayId = 0; 101 102 // *** Constructors *** 103 104 /** 105 * The same as {@code AbstractGraph(id, true, false)} 106 * 107 * @param id 108 * Identifier of the graph 109 * @see #AbstractGraph(String, boolean, boolean) 110 */ 111 public AbstractGraph(String id) { 112 this(id, true, false); 113 } 114 115 /** 116 * Creates a new graph. Subclasses must create their node and edge factories 117 * and initialize their data structures in their constructors. 118 * 119 * @param id 120 * @param strictChecking 121 * @param autoCreate 122 */ 123 public AbstractGraph(String id, boolean strictChecking, boolean autoCreate) { 124 super(id); 125 126 this.strictChecking = strictChecking; 127 this.autoCreate = autoCreate; 128 this.listeners = new GraphListeners(this); 129 } 130 131 // *** Inherited from abstract element 132 133 @Override 134 protected void attributeChanged(AttributeChangeEvent event, 135 String attribute, Object oldValue, Object newValue) { 136 listeners.sendAttributeChangedEvent(id, SourceBase.ElementType.GRAPH, 137 attribute, event, oldValue, newValue); 138 } 139 140 @Override 141 public boolean nullAttributesAreErrors() { 142 return nullAttributesAreErrors; 143 } 144 145 // *** Inherited from graph *** 146 147 // some helpers 148 149 // get node / edge by its id/index 150 151 public abstract <T extends Node> T getNode(String id); 152 153 public abstract <T extends Node> T getNode(int index); 154 155 public abstract <T extends Edge> T getEdge(String id); 156 157 public abstract <T extends Edge> T getEdge(int index); 158 159 // node and edge count, iterators and views 160 161 public abstract int getNodeCount(); 162 163 public abstract int getEdgeCount(); 164 165 public abstract <T extends Node> Iterator<T> getNodeIterator(); 166 167 public abstract <T extends Edge> Iterator<T> getEdgeIterator(); 168 169 /** 170 * This implementation uses {@link #getNodeIterator()} 171 * 172 * @see org.graphstream.graph.Graph#getEachNode() 173 */ 174 public <T extends Node> Iterable<? extends T> getEachNode() { 175 return new Iterable<T>() { 176 public Iterator<T> iterator() { 177 return getNodeIterator(); 178 } 179 }; 180 } 181 182 /** 183 * This implementation uses {@link #getEdgeIterator()} 184 * 185 * @see org.graphstream.graph.Graph#getEachEdge() 186 */ 187 public <T extends Edge> Iterable<? extends T> getEachEdge() { 188 return new Iterable<T>() { 189 public Iterator<T> iterator() { 190 return getEdgeIterator(); 191 } 192 }; 193 } 194 195 /** 196 * This implementation uses {@link #getNodeIterator()} and 197 * {@link #getNodeCount()} 198 * 199 * @see org.graphstream.graph.Graph#getNodeSet() 200 */ 201 public <T extends Node> Collection<T> getNodeSet() { 202 return new AbstractCollection<T>() { 203 public Iterator<T> iterator() { 204 return getNodeIterator(); 205 } 206 207 public int size() { 208 return getNodeCount(); 209 } 210 }; 211 } 212 213 /** 214 * This implementation uses {@link #getEdgeIterator()} and 215 * {@link #getEdgeCount()} 216 * 217 * @see org.graphstream.graph.Graph#getNodeSet() 218 */ 219 public <T extends Edge> Collection<T> getEdgeSet() { 220 return new AbstractCollection<T>() { 221 public Iterator<T> iterator() { 222 return getEdgeIterator(); 223 } 224 225 public int size() { 226 return getEdgeCount(); 227 } 228 }; 229 } 230 231 /** 232 * This implementation returns {@link #getNodeIterator()} 233 * 234 * @see java.lang.Iterable#iterator() 235 */ 236 public Iterator<Node> iterator() { 237 return getNodeIterator(); 238 } 239 240 // Factories 241 242 public NodeFactory<? extends Node> nodeFactory() { 243 return nodeFactory; 244 } 245 246 public EdgeFactory<? extends Edge> edgeFactory() { 247 return edgeFactory; 248 } 249 250 @SuppressWarnings("unchecked") 251 public void setNodeFactory(NodeFactory<? extends Node> nf) { 252 nodeFactory = (NodeFactory<? extends AbstractNode>) nf; 253 } 254 255 @SuppressWarnings("unchecked") 256 public void setEdgeFactory(EdgeFactory<? extends Edge> ef) { 257 edgeFactory = (EdgeFactory<? extends AbstractEdge>) ef; 258 } 259 260 // strict checking, autocreation, etc 261 262 public boolean isStrict() { 263 return strictChecking; 264 } 265 266 public boolean isAutoCreationEnabled() { 267 return autoCreate; 268 } 269 270 public double getStep() { 271 return step; 272 } 273 274 public void setNullAttributesAreErrors(boolean on) { 275 nullAttributesAreErrors = on; 276 } 277 278 public void setStrict(boolean on) { 279 strictChecking = on; 280 } 281 282 public void setAutoCreate(boolean on) { 283 autoCreate = on; 284 } 285 286 public void stepBegins(double time) { 287 listeners.sendStepBegins(time); 288 this.step = time; 289 } 290 291 // adding and removing elements 292 293 /* 294 * (non-Javadoc) 295 * 296 * @see org.graphstream.graph.Graph#clear() 297 */ 298 public void clear() { 299 listeners.sendGraphCleared(); 300 301 Iterator<AbstractNode> it = getNodeIterator(); 302 303 while (it.hasNext()) 304 it.next().clearCallback(); 305 306 clearCallback(); 307 clearAttributesWithNoEvent(); 308 } 309 310 /* 311 * (non-Javadoc) 312 * 313 * @see org.graphstream.graph.Graph#addNode(java.lang.String) 314 */ 315 @SuppressWarnings("unchecked") 316 public <T extends Node> T addNode(String id) { 317 AbstractNode node = getNode(id); 318 319 if (node != null) { 320 if (strictChecking) 321 throw new IdAlreadyInUseException("id \"" + id 322 + "\" already in use. Cannot create a node."); 323 return (T) node; 324 } 325 326 node = nodeFactory.newInstance(id, this); 327 addNodeCallback(node); 328 329 listeners.sendNodeAdded(id); 330 331 return (T) node; 332 } 333 334 /* 335 * (non-Javadoc) 336 * 337 * @see org.graphstream.graph.Graph#addEdge(java.lang.String, 338 * java.lang.String, java.lang.String) 339 */ 340 public <T extends Edge> T addEdge(String id, String node1, String node2) { 341 return addEdge(id, node1, node2, false); 342 } 343 344 /* 345 * (non-Javadoc) 346 * 347 * @see org.graphstream.graph.Graph#addEdge(java.lang.String, 348 * java.lang.String, java.lang.String, boolean) 349 */ 350 public <T extends Edge> T addEdge(String id, String from, String to, 351 boolean directed) { 352 return addEdge(id, (AbstractNode) getNode(from), from, 353 (AbstractNode) getNode(to), to, directed); 354 } 355 356 /* 357 * (non-Javadoc) 358 * 359 * @see org.graphstream.graph.Graph#addEdge(java.lang.String, int, int) 360 */ 361 public <T extends Edge> T addEdge(String id, int index1, int index2) { 362 return addEdge(id, index1, index2, false); 363 } 364 365 /* 366 * (non-Javadoc) 367 * 368 * @see org.graphstream.graph.Graph#addEdge(java.lang.String, int, int, 369 * boolean) 370 */ 371 public <T extends Edge> T addEdge(String id, int fromIndex, int toIndex, 372 boolean directed) { 373 return addEdge(id, getNode(fromIndex), getNode(toIndex), directed); 374 } 375 376 /* 377 * (non-Javadoc) 378 * 379 * @see org.graphstream.graph.Graph#addEdge(java.lang.String, 380 * org.graphstream.graph.Node, org.graphstream.graph.Node) 381 */ 382 public <T extends Edge> T addEdge(String id, Node node1, Node node2) { 383 return addEdge(id, node1, node2, false); 384 } 385 386 /* 387 * (non-Javadoc) 388 * 389 * @see org.graphstream.graph.Graph#addEdge(java.lang.String, 390 * org.graphstream.graph.Node, org.graphstream.graph.Node, boolean) 391 */ 392 public <T extends Edge> T addEdge(String id, Node from, Node to, 393 boolean directed) { 394 return addEdge(id, (AbstractNode) from, from.getId(), 395 (AbstractNode) to, to.getId(), directed); 396 } 397 398 /* 399 * (non-Javadoc) 400 * 401 * @see org.graphstream.graph.Graph#removeNode(java.lang.String) 402 */ 403 public <T extends Node> T removeNode(String id) { 404 AbstractNode node = getNode(id); 405 406 if (node == null) { 407 if (strictChecking) 408 throw new ElementNotFoundException("Node \"" + id 409 + "\" not found. Cannot remove it."); 410 return null; 411 } 412 413 return removeNode(node); 414 } 415 416 /* 417 * (non-Javadoc) 418 * 419 * @see org.graphstream.graph.Graph#removeNode(int) 420 */ 421 public <T extends Node> T removeNode(int index) { 422 Node node = getNode(index); 423 424 if (node == null) { 425 if (strictChecking) 426 throw new ElementNotFoundException("Node #" + index 427 + " not found. Cannot remove it."); 428 return null; 429 } 430 431 return removeNode(node); 432 } 433 434 /* 435 * (non-Javadoc) 436 * 437 * @see org.graphstream.graph.Graph#removeNode(org.graphstream.graph.Node) 438 */ 439 @SuppressWarnings("unchecked") 440 public <T extends Node> T removeNode(Node node) { 441 if (node == null) 442 return null; 443 444 removeNode((AbstractNode) node, true); 445 return (T) node; 446 } 447 448 /* 449 * (non-Javadoc) 450 * 451 * @see org.graphstream.graph.Graph#removeEdge(java.lang.String) 452 */ 453 public <T extends Edge> T removeEdge(String id) { 454 Edge edge = getEdge(id); 455 456 if (edge == null) { 457 if (strictChecking) 458 throw new ElementNotFoundException("Edge \"" + id 459 + "\" not found. Cannot remove it."); 460 return null; 461 } 462 463 return removeEdge(edge); 464 } 465 466 /* 467 * (non-Javadoc) 468 * 469 * @see org.graphstream.graph.Graph#removeEdge(int) 470 */ 471 public <T extends Edge> T removeEdge(int index) { 472 Edge edge = getEdge(index); 473 474 if (edge == null) { 475 if (strictChecking) 476 throw new ElementNotFoundException("Edge #" + index 477 + " not found. Cannot remove it."); 478 return null; 479 } 480 481 return removeEdge(edge); 482 } 483 484 /* 485 * (non-Javadoc) 486 * 487 * @see org.graphstream.graph.Graph#removeEdge(org.graphstream.graph.Edge) 488 */ 489 @SuppressWarnings("unchecked") 490 public <T extends Edge> T removeEdge(Edge edge) { 491 if (edge == null) 492 return null; 493 494 removeEdge((AbstractEdge) edge, true, true, true); 495 return (T) edge; 496 } 497 498 /* 499 * (non-Javadoc) 500 * 501 * @see org.graphstream.graph.Graph#removeEdge(java.lang.String, 502 * java.lang.String) 503 */ 504 public <T extends Edge> T removeEdge(String from, String to) { 505 Node fromNode = getNode(from); 506 Node toNode = getNode(to); 507 508 if (fromNode == null || toNode == null) { 509 if (strictChecking) 510 throw new ElementNotFoundException( 511 "Cannot remove the edge. The node \"%s\" does not exist", 512 fromNode == null ? from : to); 513 return null; 514 } 515 516 return removeEdge(fromNode, toNode); 517 } 518 519 /* 520 * (non-Javadoc) 521 * 522 * @see org.graphstream.graph.Graph#removeEdge(int, int) 523 */ 524 public <T extends Edge> T removeEdge(int fromIndex, int toIndex) { 525 Node fromNode = getNode(fromIndex); 526 Node toNode = getNode(toIndex); 527 528 if (fromNode == null || toNode == null) { 529 if (strictChecking) 530 throw new ElementNotFoundException( 531 "Cannot remove the edge. The node #%d does not exist", 532 fromNode == null ? fromIndex : toIndex); 533 return null; 534 } 535 536 return removeEdge(fromNode, toNode); 537 } 538 539 /* 540 * (non-Javadoc) 541 * 542 * @see org.graphstream.graph.Graph#removeEdge(org.graphstream.graph.Node, 543 * org.graphstream.graph.Node) 544 */ 545 public <T extends Edge> T removeEdge(Node node1, Node node2) { 546 AbstractEdge edge = node1.getEdgeToward(node2); 547 548 if (edge == null) { 549 if (strictChecking) 550 throw new ElementNotFoundException( 551 "There is no edge from \"%s\" to \"%s\". Cannot remove it.", 552 node1.getId(), node2.getId()); 553 return null; 554 } 555 556 return removeEdge(edge); 557 } 558 559 // *** Sinks, sources etc. *** 560 561 /* 562 * (non-Javadoc) 563 * 564 * @see org.graphstream.graph.Graph#attributeSinks() 565 */ 566 public Iterable<AttributeSink> attributeSinks() { 567 return listeners.attributeSinks(); 568 } 569 570 /* 571 * *(non-Javadoc) 572 * 573 * @see org.graphstream.graph.Graph#elementSinks() 574 */ 575 public Iterable<ElementSink> elementSinks() { 576 return listeners.elementSinks(); 577 } 578 579 /* 580 * *(non-Javadoc) 581 * 582 * @see 583 * org.graphstream.stream.Source#addAttributeSink(org.graphstream.stream 584 * .AttributeSink) 585 */ 586 public void addAttributeSink(AttributeSink sink) { 587 listeners.addAttributeSink(sink); 588 } 589 590 /* 591 * (non-Javadoc) 592 * 593 * @see org.graphstream.stream.Source#addElementSink(org.graphstream.stream. 594 * ElementSink) 595 */ 596 public void addElementSink(ElementSink sink) { 597 listeners.addElementSink(sink); 598 } 599 600 /* 601 * (non-Javadoc) 602 * 603 * @see org.graphstream.stream.Source#addSink(org.graphstream.stream.Sink) 604 */ 605 public void addSink(Sink sink) { 606 listeners.addSink(sink); 607 } 608 609 /* 610 * (non-Javadoc) 611 * 612 * @see org.graphstream.stream.Source#clearAttributeSinks() 613 */ 614 public void clearAttributeSinks() { 615 listeners.clearAttributeSinks(); 616 } 617 618 /* 619 * *(non-Javadoc) 620 * 621 * @see org.graphstream.stream.Source#clearElementSinks() 622 */ 623 public void clearElementSinks() { 624 listeners.clearElementSinks(); 625 } 626 627 /* 628 * *(non-Javadoc) 629 * 630 * @see org.graphstream.stream.Source#clearSinks() 631 */ 632 public void clearSinks() { 633 listeners.clearSinks(); 634 } 635 636 /* 637 * (non-Javadoc) 638 * 639 * @see 640 * org.graphstream.stream.Source#removeAttributeSink(org.graphstream.stream 641 * .AttributeSink) 642 */ 643 public void removeAttributeSink(AttributeSink sink) { 644 listeners.removeAttributeSink(sink); 645 } 646 647 /* 648 * (non-Javadoc) 649 * 650 * @see 651 * org.graphstream.stream.Source#removeElementSink(org.graphstream.stream 652 * .ElementSink) 653 */ 654 public void removeElementSink(ElementSink sink) { 655 listeners.removeElementSink(sink); 656 } 657 658 /* 659 * (non-Javadoc) 660 * 661 * @see 662 * org.graphstream.stream.Source#removeSink(org.graphstream.stream.Sink) 663 */ 664 public void removeSink(Sink sink) { 665 listeners.removeSink(sink); 666 } 667 668 public void edgeAttributeAdded(String sourceId, long timeId, String edgeId, 669 String attribute, Object value) { 670 listeners 671 .edgeAttributeAdded(sourceId, timeId, edgeId, attribute, value); 672 } 673 674 public void edgeAttributeChanged(String sourceId, long timeId, 675 String edgeId, String attribute, Object oldValue, Object newValue) { 676 listeners.edgeAttributeChanged(sourceId, timeId, edgeId, attribute, 677 oldValue, newValue); 678 } 679 680 public void edgeAttributeRemoved(String sourceId, long timeId, 681 String edgeId, String attribute) { 682 listeners.edgeAttributeRemoved(sourceId, timeId, edgeId, attribute); 683 } 684 685 public void graphAttributeAdded(String sourceId, long timeId, 686 String attribute, Object value) { 687 listeners.graphAttributeAdded(sourceId, timeId, attribute, value); 688 } 689 690 public void graphAttributeChanged(String sourceId, long timeId, 691 String attribute, Object oldValue, Object newValue) { 692 listeners.graphAttributeChanged(sourceId, timeId, attribute, oldValue, 693 newValue); 694 } 695 696 public void graphAttributeRemoved(String sourceId, long timeId, 697 String attribute) { 698 listeners.graphAttributeRemoved(sourceId, timeId, attribute); 699 } 700 701 public void nodeAttributeAdded(String sourceId, long timeId, String nodeId, 702 String attribute, Object value) { 703 listeners 704 .nodeAttributeAdded(sourceId, timeId, nodeId, attribute, value); 705 } 706 707 public void nodeAttributeChanged(String sourceId, long timeId, 708 String nodeId, String attribute, Object oldValue, Object newValue) { 709 listeners.nodeAttributeChanged(sourceId, timeId, nodeId, attribute, 710 oldValue, newValue); 711 } 712 713 public void nodeAttributeRemoved(String sourceId, long timeId, 714 String nodeId, String attribute) { 715 listeners.nodeAttributeRemoved(sourceId, timeId, nodeId, attribute); 716 } 717 718 public void edgeAdded(String sourceId, long timeId, String edgeId, 719 String fromNodeId, String toNodeId, boolean directed) { 720 listeners.edgeAdded(sourceId, timeId, edgeId, fromNodeId, toNodeId, 721 directed); 722 } 723 724 public void edgeRemoved(String sourceId, long timeId, String edgeId) { 725 listeners.edgeRemoved(sourceId, timeId, edgeId); 726 } 727 728 public void graphCleared(String sourceId, long timeId) { 729 listeners.graphCleared(sourceId, timeId); 730 } 731 732 public void nodeAdded(String sourceId, long timeId, String nodeId) { 733 listeners.nodeAdded(sourceId, timeId, nodeId); 734 } 735 736 public void nodeRemoved(String sourceId, long timeId, String nodeId) { 737 listeners.nodeRemoved(sourceId, timeId, nodeId); 738 } 739 740 public void stepBegins(String sourceId, long timeId, double step) { 741 listeners.stepBegins(sourceId, timeId, step); 742 } 743 744 // display, read, write 745 746 public Viewer display() { 747 return display(true); 748 } 749 750 public Viewer display(boolean autoLayout) { 751 Viewer viewer = new Viewer(this, 752 Viewer.ThreadingModel.GRAPH_IN_ANOTHER_THREAD); 753 GraphRenderer renderer = Viewer.newGraphRenderer(); 754 viewer.addView(Viewer.DEFAULT_VIEW_ID, renderer); 755 if (autoLayout) { 756 Layout layout = Layouts.newLayoutAlgorithm(); 757 viewer.enableAutoLayout(layout); 758 } 759 return viewer; 760 } 761 762 public void read(FileSource input, String filename) throws IOException, 763 GraphParseException { 764 input.readAll(filename); 765 } 766 767 public void read(String filename) throws IOException, GraphParseException, 768 ElementNotFoundException { 769 FileSource input = FileSourceFactory.sourceFor(filename); 770 if (input != null) { 771 input.addSink(this); 772 read(input, filename); 773 input.removeSink(this); 774 } else { 775 throw new IOException("No source reader for " + filename); 776 } 777 } 778 779 public void write(FileSink output, String filename) throws IOException { 780 output.writeAll(this, filename); 781 } 782 783 public void write(String filename) throws IOException { 784 FileSink output = FileSinkFactory.sinkFor(filename); 785 if (output != null) { 786 write(output, filename); 787 } else { 788 throw new IOException("No sink writer for " + filename); 789 } 790 } 791 792 /* 793 * (non-Javadoc) 794 * 795 * @see org.graphstream.stream.Replayable#getReplayController() 796 */ 797 public Replayable.Controller getReplayController() { 798 return new GraphReplayController(); 799 } 800 801 // *** callbacks maintaining user's data structure 802 803 /** 804 * This method is automatically called when a new node is created. 805 * Subclasses must add the new node to their data structure and to set its 806 * index correctly. 807 * 808 * @param node 809 * the node to be added 810 */ 811 protected abstract void addNodeCallback(AbstractNode node); 812 813 /** 814 * This method is automatically called when a new edge is created. 815 * Subclasses must add the new edge to their data structure and to set its 816 * index correctly. 817 * 818 * @param edge 819 * the edge to be added 820 */ 821 protected abstract void addEdgeCallback(AbstractEdge edge); 822 823 /** 824 * This method is automatically called when a node is removed. Subclasses 825 * must remove the node from their data structures and to re-index other 826 * node(s) so that node indices remain coherent. 827 * 828 * @param node 829 * the node to be removed 830 */ 831 protected abstract void removeNodeCallback(AbstractNode node); 832 833 /** 834 * This method is automatically called when an edge is removed. Subclasses 835 * must remove the edge from their data structures and re-index other 836 * edge(s) so that edge indices remain coherent. 837 * 838 * @param edge 839 * the edge to be removed 840 */ 841 protected abstract void removeEdgeCallback(AbstractEdge edge); 842 843 /** 844 * This method is automatically called when the graph is cleared. Subclasses 845 * must remove all the nodes and all the edges from their data structures. 846 */ 847 protected abstract void clearCallback(); 848 849 // *** _ methods *** 850 851 // Why do we pass both the ids and the references of the endpoints here? 852 // When the caller knows the references it's stupid to call getNode(id) 853 // here. If the node does not exist the reference will be null. 854 // And if autoCreate is on, we need also the id. Sad but true! 855 @SuppressWarnings("unchecked") 856 protected <T extends Edge> T addEdge(String edgeId, AbstractNode src, 857 String srcId, AbstractNode dst, String dstId, boolean directed) { 858 AbstractEdge edge = getEdge(edgeId); 859 if (edge != null) { 860 if (strictChecking) 861 throw new IdAlreadyInUseException("id \"" + edgeId 862 + "\" already in use. Cannot create an edge."); 863 if ((edge.getSourceNode() == src && edge.getTargetNode() == dst) 864 || (!directed && edge.getTargetNode() == src && edge 865 .getSourceNode() == dst)) 866 return (T) edge; 867 return null; 868 } 869 870 if (src == null || dst == null) { 871 if (strictChecking) 872 throw new ElementNotFoundException( 873 String.format( 874 "Cannot create edge %s[%s-%s%s]. Node '%s' does not exist.", 875 edgeId, srcId, directed ? ">" : "-", dstId, 876 src == null ? srcId : dstId)); 877 if (!autoCreate) 878 return null; 879 if (src == null) 880 src = addNode(srcId); 881 if (dst == null) 882 dst = addNode(dstId); 883 } 884 // at this point edgeId is not in use and both src and dst are not null 885 edge = edgeFactory.newInstance(edgeId, src, dst, directed); 886 // see if the endpoints accept the edge 887 if (!src.addEdgeCallback(edge)) { 888 if (strictChecking) 889 throw new EdgeRejectedException("Edge " + edge 890 + " was rejected by node " + src); 891 return null; 892 } 893 // note that for loop edges the callback is called only once 894 if (src != dst && !dst.addEdgeCallback(edge)) { 895 // the edge is accepted by src but rejected by dst 896 // so we have to remove it from src 897 src.removeEdgeCallback(edge); 898 if (strictChecking) 899 throw new EdgeRejectedException("Edge " + edge 900 + " was rejected by node " + dst); 901 return null; 902 } 903 904 // now we can finally add it 905 addEdgeCallback(edge); 906 907 listeners.sendEdgeAdded(edgeId, srcId, dstId, directed); 908 909 return (T) edge; 910 } 911 912 // helper for removeNode_ 913 private void removeAllEdges(AbstractNode node) { 914 // first check if the EdgeIterator of node supports remove 915 // if this is the case, we will use it, generally it will be much more 916 // efficient 917 Iterator<AbstractEdge> edgeIt = node.getEdgeIterator(); 918 boolean supportsRemove = true; 919 if (!edgeIt.hasNext()) 920 return; 921 try { 922 edgeIt.next(); 923 edgeIt.remove(); 924 } catch (UnsupportedOperationException e) { 925 supportsRemove = false; 926 } 927 if (supportsRemove) 928 while (edgeIt.hasNext()) { 929 edgeIt.next(); 930 edgeIt.remove(); 931 } 932 else 933 while (node.getDegree() > 0) 934 removeEdge(node.getEdge(0)); 935 } 936 937 // *** Methods for iterators *** 938 939 /** 940 * This method is similar to {@link #removeNode(Node)} but allows to control 941 * if {@link #removeNodeCallback(AbstractNode)} is called or not. It is 942 * useful for iterators supporting {@link java.util.Iterator#remove()} who 943 * want to update the data structures by their owns. 944 * 945 * @param node 946 * the node to be removed 947 * @param graphCallback 948 * if {@code false}, {@code removeNodeCallback(node)} is not 949 * called 950 */ 951 protected void removeNode(AbstractNode node, boolean graphCallback) { 952 if (node == null) 953 return; 954 955 removeAllEdges(node); 956 listeners.sendNodeRemoved(node.getId()); 957 958 if (graphCallback) 959 removeNodeCallback(node); 960 } 961 962 /** 963 * This method is similar to {@link #removeEdge(Edge)} but allows to control 964 * if different callbacks are called or not. It is useful for iterators 965 * supporting {@link java.util.Iterator#remove()} who want to update the 966 * data structures by their owns. 967 * 968 * @param edge 969 * the edge to be removed 970 * @param graphCallback 971 * if {@code false}, {@link #removeEdgeCallback(AbstractEdge)} of 972 * the graph is not called 973 * @param sourceCallback 974 * if {@code false}, 975 * {@link AbstractNode#removeEdgeCallback(AbstractEdge)} is not 976 * called for the source node of the edge 977 * @param targetCallback 978 * if {@code false}, 979 * {@link AbstractNode#removeEdgeCallback(AbstractEdge)} is not 980 * called for the target node of the edge 981 */ 982 protected void removeEdge(AbstractEdge edge, boolean graphCallback, 983 boolean sourceCallback, boolean targetCallback) { 984 if (edge == null) 985 return; 986 987 AbstractNode src = edge.getSourceNode(); 988 AbstractNode dst = edge.getTargetNode(); 989 990 listeners.sendEdgeRemoved(edge.getId()); 991 992 if (sourceCallback) 993 src.removeEdgeCallback(edge); 994 995 if (src != dst && targetCallback) 996 dst.removeEdgeCallback(edge); 997 998 if (graphCallback) 999 removeEdgeCallback(edge); 1000 } 1001 1002 class GraphReplayController extends SourceBase implements 1003 Replayable.Controller { 1004 GraphReplayController() { 1005 super(AbstractGraph.this.id + "replay"); 1006 } 1007 1008 /* 1009 * (non-Javadoc) 1010 * 1011 * @see org.graphstream.stream.Replayable.Controller#replay() 1012 */ 1013 public void replay() { 1014 String sourceId = String.format("%s-replay-%x", id, replayId++); 1015 replay(sourceId); 1016 } 1017 1018 /* 1019 * (non-Javadoc) 1020 * 1021 * @see 1022 * org.graphstream.stream.Replayable.Controller#replay(java.lang.String) 1023 */ 1024 public void replay(String sourceId) { 1025 for (String key : getAttributeKeySet()) 1026 sendGraphAttributeAdded(sourceId, key, getAttribute(key)); 1027 1028 for (int i = 0; i < getNodeCount(); i++) { 1029 Node node = getNode(i); 1030 String nodeId = node.getId(); 1031 1032 sendNodeAdded(sourceId, nodeId); 1033 1034 if (node.getAttributeCount() > 0) 1035 for (String key : node.getAttributeKeySet()) 1036 sendNodeAttributeAdded(sourceId, nodeId, key, 1037 node.getAttribute(key)); 1038 } 1039 1040 for (int i = 0; i < getEdgeCount(); i++) { 1041 Edge edge = getEdge(i); 1042 String edgeId = edge.getId(); 1043 1044 sendEdgeAdded(sourceId, edgeId, edge.getNode0().getId(), edge 1045 .getNode1().getId(), edge.isDirected()); 1046 1047 if (edge.getAttributeCount() > 0) 1048 for (String key : edge.getAttributeKeySet()) 1049 sendEdgeAttributeAdded(sourceId, edgeId, key, 1050 edge.getAttribute(key)); 1051 } 1052 } 1053 } 1054}