| 1 package prefuse.data; 2 3 import java.util.Iterator ; 4 5 import prefuse.data.column.Column; 6 import prefuse.data.event.ColumnListener; 7 import prefuse.data.event.EventConstants; 8 import prefuse.data.event.GraphListener; 9 import prefuse.data.event.TableListener; 10 import prefuse.data.expression.Predicate; 11 import prefuse.data.tuple.CompositeTupleSet; 12 import prefuse.data.tuple.TableEdge; 13 import prefuse.data.tuple.TableNode; 14 import prefuse.data.tuple.TupleManager; 15 import prefuse.data.tuple.TupleSet; 16 import prefuse.data.util.Index; 17 import prefuse.data.util.NeighborIterator; 18 import prefuse.util.PrefuseConfig; 19 import prefuse.util.TypeLib; 20 import prefuse.util.collections.CompositeIntIterator; 21 import prefuse.util.collections.CompositeIterator; 22 import prefuse.util.collections.CopyOnWriteArrayList; 23 import prefuse.util.collections.IntArrayIterator; 24 import prefuse.util.collections.IntIterator; 25 26 78 public class Graph extends CompositeTupleSet { 79 80 81 public static final int INEDGES = 0; 82 83 public static final int OUTEDGES = 1; 84 85 public static final int UNDIRECTED = 2; 86 87 88 public static final String DEFAULT_NODE_KEY 89 = PrefuseConfig.get("data.graph.nodeKey"); 90 91 public static final String DEFAULT_SOURCE_KEY 92 = PrefuseConfig.get("data.graph.sourceKey"); 93 94 public static final String DEFAULT_TARGET_KEY 95 = PrefuseConfig.get("data.graph.targetKey"); 96 97 public static final String NODES 98 = PrefuseConfig.get("data.graph.nodeGroup"); 99 100 public static final String EDGES 101 = PrefuseConfig.get("data.graph.edgeGroup"); 102 103 105 106 protected Table m_links; 107 108 protected TupleManager m_nodeTuples; 109 110 protected TupleManager m_edgeTuples; 111 112 113 protected boolean m_directed = false; 114 115 protected SpanningTree m_spanning = null; 116 117 118 protected String m_nkey; 119 120 protected String m_skey; 121 122 protected String m_tkey; 123 124 protected Index m_nidx; 125 126 protected boolean m_longKey = false; 127 128 private Listener m_listener; 129 130 private CopyOnWriteArrayList m_listeners = new CopyOnWriteArrayList(); 131 132 135 138 public Graph() { 139 this(false); 140 } 141 142 146 public Graph(boolean directed) { 147 this(new Table(), directed); 148 } 149 150 157 public Graph(Table nodes, boolean directed) { 158 this(nodes, directed, DEFAULT_NODE_KEY, 159 DEFAULT_SOURCE_KEY, DEFAULT_TARGET_KEY); 160 } 161 162 175 public Graph(Table nodes, boolean directed, String nodeKey, 176 String sourceKey, String targetKey) { 177 Table edges = new Table(); 178 edges.addColumn(sourceKey, int.class, new Integer (-1)); 179 edges.addColumn(targetKey, int.class, new Integer (-1)); 180 init(nodes, edges, directed, nodeKey, sourceKey, targetKey); 181 } 182 183 192 public Graph(Table nodes, Table edges, boolean directed) { 193 this(nodes, edges, directed, 194 DEFAULT_NODE_KEY, DEFAULT_SOURCE_KEY, DEFAULT_TARGET_KEY); 195 } 196 197 210 public Graph(Table nodes, Table edges, boolean directed, 211 String sourceKey, String targetKey) 212 { 213 init(nodes, edges, directed, DEFAULT_NODE_KEY, sourceKey, targetKey); 214 } 215 216 230 public Graph(Table nodes, Table edges, boolean directed, 231 String nodeKey, String sourceKey, String targetKey) 232 { 233 init(nodes, edges, directed, nodeKey, sourceKey, targetKey); 234 } 235 236 239 250 protected void init(Table nodes, Table edges, boolean directed, 251 String nodeKey, String sourceKey, String targetKey) 252 { 253 if ( (nodeKey!=null && 255 !TypeLib.isIntegerType(nodes.getColumnType(nodeKey))) || 256 !TypeLib.isIntegerType(edges.getColumnType(sourceKey)) || 257 !TypeLib.isIntegerType(edges.getColumnType(targetKey)) ) 258 { 259 throw new IllegalArgumentException ( 260 "Incompatible column types for graph keys"); 261 } 262 263 removeAllSets(); 264 super.addSet(EDGES, edges); 265 super.addSet(NODES, nodes); 266 267 m_directed = directed; 268 269 m_nkey = nodeKey; 272 m_skey = sourceKey; 273 m_tkey = targetKey; 274 275 if ( nodeKey != null ) { 277 if ( nodes.getColumnType(nodeKey) == long.class ) 278 m_longKey = true; 279 nodes.index(nodeKey); 280 m_nidx = nodes.getIndex(nodeKey); 281 } 282 283 if ( m_nodeTuples == null ) 285 m_nodeTuples = new TupleManager(nodes, this, TableNode.class); 286 m_edgeTuples = new TupleManager(edges, this, TableEdge.class); 287 288 initLinkTable(); 290 291 if ( m_listener == null ) 293 m_listener = new Listener(); 294 nodes.addTableListener(m_listener); 295 edges.addTableListener(m_listener); 296 m_listener.setEdgeTable(edges); 297 } 298 299 305 public void setTupleManagers(TupleManager ntm, TupleManager etm) { 306 if ( !Node.class.isAssignableFrom(ntm.getTupleType()) ) 307 throw new IllegalArgumentException ("The provided node " + 308 "TupleManager must generate tuples that implement " + 309 "the Node interface."); 310 if ( !Edge.class.isAssignableFrom(etm.getTupleType()) ) 311 throw new IllegalArgumentException ("The provided edge " + 312 "TupleManager must generate tuples that implement " + 313 "the Edge interface."); 314 m_nodeTuples = ntm; 315 m_edgeTuples = etm; 316 } 317 318 322 public void dispose() { 323 getNodeTable().removeTableListener(m_listener); 324 getEdgeTable().removeTableListener(m_listener); 325 } 326 327 332 public void setEdgeTable(Table edges) { 333 Table oldEdges = getEdgeTable(); 334 oldEdges.removeTableListener(m_listener); 335 m_edgeTuples.invalidateAll(); 336 m_links.clear(); 337 338 init(getNodeTable(), edges, m_directed, m_nkey, m_skey, m_tkey); 339 } 340 341 344 347 protected void initLinkTable() { 348 m_links = createLinkTable(); 350 351 IntIterator edges = getEdgeTable().rows(); 352 while ( edges.hasNext() ) { 353 updateDegrees(edges.nextInt(), 1); 354 } 355 } 356 357 361 protected Table createLinkTable() { 362 return LINKS_SCHEMA.instantiate(getNodeTable().getMaximumRow()+1); 363 } 364 365 371 protected void updateDegrees(int e, int incr) { 372 if ( !getEdgeTable().isValidRow(e) ) return; 373 int s = getSourceNode(e); 374 int t = getTargetNode(e); 375 if ( s < 0 || t < 0 ) return; 376 updateDegrees(e, s, t, incr); 377 if ( incr < 0 ) { 378 m_edgeTuples.invalidate(e); 379 } 380 } 381 382 390 protected void updateDegrees(int e, int s, int t, int incr) { 391 int od = m_links.getInt(s, OUTDEGREE); 392 int id = m_links.getInt(t, INDEGREE); 393 if ( incr > 0 ) { 395 addLink(OUTLINKS, od, s, e); 397 addLink(INLINKS, id, t, e); 398 } else if ( incr < 0 ) { 399 remLink(OUTLINKS, od, s, e); 401 remLink(INLINKS, id, t, e); 402 } 403 m_links.setInt(s, OUTDEGREE, od+incr); 405 m_links.setInt(t, INDEGREE, id+incr); 406 m_spanning = null; 408 } 409 410 417 protected void addLink(String field, int len, int n, int e) { 418 int[] array = (int[])m_links.get(n, field); 419 if ( array == null ) { 420 array = new int[] {e}; 421 m_links.set(n, field, array); 422 return; 423 } else if ( len == array.length ) { 424 int[] narray = new int[Math.max(3*array.length/2, len+1)]; 425 System.arraycopy(array, 0, narray, 0, array.length); 426 array = narray; 427 m_links.set(n, field, array); 428 } 429 array[len] = e; 430 } 431 432 440 protected boolean remLink(String field, int len, int n, int e) { 441 int[] array = (int[])m_links.get(n, field); 442 for ( int i=0; i<len; ++i ) { 443 if ( array[i] == e ) { 444 System.arraycopy(array, i+1, array, i, len-i-1); 445 return true; 446 } 447 } 448 return false; 449 } 450 451 456 protected void updateNodeData(int r, boolean added) { 457 if ( added ) { 458 m_links.addRow(); 459 } else { 460 m_nodeTuples.invalidate(r); 461 m_links.removeRow(r); 462 } 463 } 464 465 468 472 public String getNodeKeyField() { 473 return m_nkey; 474 } 475 476 480 public String getEdgeSourceField() { 481 return m_skey; 482 } 483 484 488 public String getEdgeTargetField() { 489 return m_tkey; 490 } 491 492 498 public long getKey(int node) { 499 return m_nkey == null ? node : getNodeTable().getLong(node, m_nkey); 500 } 501 502 508 public int getNodeIndex(long key) { 509 if ( m_nidx == null ) { 510 return (int)key; 511 } else { 512 int idx = m_longKey ? m_nidx.get(key) : m_nidx.get((int)key); 513 return idx<0 ? -1 : idx; 514 } 515 } 516 517 520 524 public int addNodeRow() { 525 return getNodeTable().addRow(); 526 } 527 528 532 public Node addNode() { 533 int nrow = addNodeRow(); 534 return (Node)m_nodeTuples.getTuple(nrow); 535 } 536 537 544 public int addEdge(int s, int t) { 545 long key1 = getKey(s); 547 long key2 = getKey(t); 548 549 Table edges = getEdgeTable(); 551 int r = edges.addRow(); 552 if ( m_longKey ) { 553 edges.setLong(r, m_skey, key1); 554 edges.setLong(r, m_tkey, key2); 555 } else { 556 edges.setInt(r, m_skey, (int)key1); 557 edges.setInt(r, m_tkey, (int)key2); 558 } 559 return r; 560 } 561 562 568 public Edge addEdge(Node s, Node t) { 569 nodeCheck(s, true); 570 nodeCheck(t, true); 571 int e = addEdge(s.getRow(), t.getRow()); 572 return getEdge(e); 573 } 574 575 581 public boolean removeNode(int node) { 582 Table nodeTable = getNodeTable(); 583 if ( nodeTable.isValidRow(node) ) { 584 int id = getInDegree(node); 585 if ( id > 0 ) { 586 int[] links = (int[])m_links.get(node, INLINKS); 587 for ( int i=id; --i>=0; ) 588 removeEdge(links[i]); 589 } 590 int od = getOutDegree(node); 591 if ( od > 0 ) { 592 int[] links = (int[])m_links.get(node, OUTLINKS); 593 for ( int i=od; --i>=0; ) 594 removeEdge(links[i]); 595 } 596 } 597 return nodeTable.removeRow(node); 598 } 599 600 606 public boolean removeNode(Node n) { 607 nodeCheck(n, true); 608 return removeNode(n.getRow()); 609 } 610 611 617 public boolean removeEdge(int edge) { 618 return getEdgeTable().removeRow(edge); 619 } 620 621 627 public boolean removeEdge(Edge e) { 628 edgeCheck(e, true); 629 return removeEdge(e.getRow()); 630 } 631 632 635 protected void clearEdges() { 636 getEdgeTable().clear(); 637 } 638 639 642 649 protected boolean nodeCheck(Node n, boolean throwException) { 650 if ( !n.isValid() ) { 651 if ( throwException ) { 652 throw new IllegalArgumentException ( 653 "Node must be valid."); 654 } 655 return false; 656 } 657 Graph ng = n.getGraph(); 658 if ( ng != this && ng.m_spanning != this ) { 659 if ( throwException ) { 660 throw new IllegalArgumentException ( 661 "Node must be part of this Graph."); 662 } 663 return false; 664 } 665 return true; 666 } 667 668 674 public TupleSet getNodes() { 675 return getSet(NODES); 676 } 677 678 682 public Table getNodeTable() { 683 return (Table)getSet(NODES); 684 } 685 686 690 public int getNodeCount() { 691 return getNodeTable().getRowCount(); 692 } 693 694 699 public Node getNode(int n) { 700 return (Node)m_nodeTuples.getTuple(n); 701 } 702 703 710 public Node getNodeFromKey(long key) { 711 int n = getNodeIndex(key); 712 return (n<0 ? null : getNode(n) ); 713 } 714 715 721 public int getInDegree(int node) { 722 return m_links.getInt(node, INDEGREE); 723 } 724 725 731 public int getInDegree(Node n) { 732 nodeCheck(n, true); 733 return getInDegree(n.getRow()); 734 } 735 736 742 public int getOutDegree(int node) { 743 return m_links.getInt(node, OUTDEGREE); 744 } 745 746 752 public int getOutDegree(Node n) { 753 nodeCheck(n, true); 754 return getOutDegree(n.getRow()); 755 } 756 757 763 public int getDegree(int node) { 764 return getInDegree(node) + getOutDegree(node); 765 } 766 767 773 public int getDegree(Node n) { 774 nodeCheck(n, true); 775 return getDegree(n.getRow()); 776 } 777 778 781 785 public boolean isDirected() { 786 return m_directed; 787 } 788 789 796 protected boolean edgeCheck(Edge e, boolean throwException) { 797 if ( !e.isValid() ) { 798 if ( throwException ) { 799 throw new IllegalArgumentException ( 800 "Edge must be valid."); 801 } 802 return false; 803 } 804 if ( e.getGraph() != this ) { 805 if ( throwException ) { 806 throw new IllegalArgumentException ( 807 "Edge must be part of this Graph."); 808 } 809 return false; 810 } 811 return true; 812 } 813 814 820 public TupleSet getEdges() { 821 return getSet(EDGES); 822 } 823 824 828 public Table getEdgeTable() { 829 return (Table)getSet(EDGES); 830 } 831 835 public int getEdgeCount() { 836 return getEdgeTable().getRowCount(); 837 } 838 839 844 public Edge getEdge(int e) { 845 return ( e < 0 ? null : (Edge)m_edgeTuples.getTuple(e) ); 846 } 847 848 853 public int getEdge(int source, int target) { 854 int outd = getOutDegree(source); 855 if ( outd > 0 ) { 856 int[] edges = (int[])m_links.get(source, OUTLINKS); 857 for ( int i=0; i<outd; ++i ) { 858 if ( getTargetNode(edges[i]) == target ) 859 return edges[i]; 860 } 861 } 862 return -1; 863 } 864 865 874 public Edge getEdge(Node source, Node target) { 875 nodeCheck(source, true); 876 nodeCheck(target, true); 877 return getEdge(getEdge(source.getRow(), target.getRow())); 878 } 879 880 886 public int getSourceNode(int edge) { 887 return getNodeIndex(getEdgeTable().getLong(edge, m_skey)); 888 } 889 890 895 public Node getSourceNode(Edge e) { 896 edgeCheck(e, true); 897 return getNode(getSourceNode(e.getRow())); 898 } 899 900 906 public int getTargetNode(int edge) { 907 return getNodeIndex(getEdgeTable().getLong(edge, m_tkey)); 908 } 909 910 915 public Node getTargetNode(Edge e) { 916 edgeCheck(e, true); 917 return getNode(getTargetNode(e.getRow())); 918 } 919 920 928 public int getAdjacentNode(int edge, int node) { 929 int s = getSourceNode(edge); 930 int d = getTargetNode(edge); 931 932 if ( s == node ) { 933 return d; 934 } else if ( d == node )
|