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 ) { 935 return s; 936 } else { 937 throw new IllegalArgumentException ( 938 "Edge is not incident on the input node."); 939 } 940 } 941 942 950 public Node getAdjacentNode(Edge e, Node n) { 951 edgeCheck(e, true); 952 nodeCheck(n, true); 953 return getNode(getAdjacentNode(e.getRow(), n.getRow())); 954 } 955 956 959 961 965 public IntIterator nodeRows() { 966 return getNodeTable().rows(); 967 } 968 969 973 public IntIterator edgeRows() { 974 return getEdgeTable().rows(); 975 } 976 977 983 public IntIterator edgeRows(int node) { 984 return edgeRows(node, UNDIRECTED); 985 } 986 987 997 public IntIterator edgeRows(int node, int direction) { 998 if ( direction==OUTEDGES ) { 999 int[] outedges = (int[])m_links.get(node, OUTLINKS); 1000 return new IntArrayIterator(outedges, 0, getOutDegree(node)); 1001 } else if ( direction==INEDGES ) { 1002 int[] inedges = (int[])m_links.get(node, INLINKS); 1003 return new IntArrayIterator(inedges, 0, getInDegree(node)); 1004 } else if ( direction==UNDIRECTED ) { 1005 return new CompositeIntIterator( 1006 edgeRows(node, OUTEDGES), edgeRows(node, INEDGES)); 1007 } else { 1008 throw new IllegalArgumentException ("Unrecognized edge type: " 1009 + direction + ". Type should be one of Graph.OUTEDGES, " 1010 + "Graoh.INEDGES, or Graph.ALL"); 1011 } 1012 } 1013 1014 1020 public IntIterator inEdgeRows(int node) { 1021 return edgeRows(node, INEDGES); 1022 } 1023 1024 1030 public IntIterator outEdgeRows(int node) { 1031 return edgeRows(node, OUTEDGES); 1032 } 1033 1034 1036 1040 public Iterator nodes() { 1041 return m_nodeTuples.iterator(nodeRows()); 1042 } 1043 1044 1049 public Iterator neighbors(Node n) { 1050 return new NeighborIterator(n, edges(n)); 1051 } 1052 1053 1058 public Iterator inNeighbors(Node n) { 1059 return new NeighborIterator(n, inEdges(n)); 1060 } 1061 1062 1067 public Iterator outNeighbors(Node n) { 1068 return new NeighborIterator(n, outEdges(n)); 1069 } 1070 1071 1075 public Iterator edges() { 1076 return m_edgeTuples.iterator(edgeRows()); 1077 } 1078 1079 1084 public Iterator edges(Node node) { 1085 nodeCheck(node, true); 1086 return m_edgeTuples.iterator(edgeRows(node.getRow(), UNDIRECTED)); 1087 } 1088 1089 1094 public Iterator inEdges(Node node) { 1095 nodeCheck(node, true); 1096 return m_edgeTuples.iterator(inEdgeRows(node.getRow())); 1097 } 1098 1099 1105 public Iterator outEdges(Node node) { 1106 nodeCheck(node, true); 1107 return m_edgeTuples.iterator(outEdgeRows(node.getRow())); 1108 } 1109 1110 1113 1117 public void clear() { 1118 m_nodeTuples.invalidateAll(); 1119 m_edgeTuples.invalidateAll(); 1120 super.clear(); 1121 m_links.clear(); 1122 } 1123 1124 1128 public boolean removeTuple(Tuple t) { 1129 if ( t instanceof Node ) { 1131 return removeNode((Node)t); 1132 } else if ( t instanceof Edge ) { 1133 return removeEdge((Edge)t); 1134 } else { 1135 throw new IllegalArgumentException ( 1136 "Input tuple must be part of this graph"); 1137 } 1138 } 1139 1140 1144 public Iterator tuples(Predicate filter) { 1145 if ( filter == null ) { 1146 return tuples(); 1147 } else { 1148 return new CompositeIterator( 1149 m_edgeTuples.iterator(getEdgeTable().rows(filter)), 1150 m_nodeTuples.iterator(getNodeTable().rows(filter))); 1151 } 1152 } 1153 1154 1159 public Iterator tuples() { 1160 return new CompositeIterator(edges(), nodes()); 1161 } 1162 1163 1166 1178 public Tree getSpanningTree() { 1179 if ( m_spanning == null ) 1180 return getSpanningTree((Node)nodes().next()); 1181 else 1182 return m_spanning; 1183 } 1184 1185 1199 public Tree getSpanningTree(Node root) { 1200 nodeCheck(root, true); 1201 if ( m_spanning == null ) { 1202 m_spanning = new SpanningTree(this, root); 1203 } else if ( m_spanning.getRoot() != root ) { 1204 m_spanning.buildSpanningTree(root); 1205 } 1206 return m_spanning; 1207 } 1208 1209 1222 public void clearSpanningTree() { 1223 m_spanning = null; 1224 } 1225 1226 1229 1233 public void addGraphModelListener(GraphListener listnr) { 1234 if ( !m_listeners.contains(listnr) ) 1235 m_listeners.add(listnr); 1236 } 1237 1238 1242 public void removeGraphModelListener(GraphListener listnr) { 1243 m_listeners.remove(listnr); 1244 } 1245 1246 1260 protected void fireGraphEvent(Table t, 1261 int first, int last, int col, int type) 1262 { 1263 String table = (t==getNodeTable() ? NODES : EDGES); 1264 1265 if ( type != EventConstants.UPDATE ) { 1266 fireTupleEvent(t, first, last, type); 1268 } 1269 1270 if ( !m_listeners.isEmpty() ) { 1271 Object [] lstnrs = m_listeners.getArray(); 1273 for ( int i=0; i<lstnrs.length; ++i ) { 1274 ((GraphListener)lstnrs[i]).graphChanged( 1275 this, table, first, last, col, type); 1276 } 1277 } 1278 } 1279 1280 1283 1287 protected class Listener implements TableListener, ColumnListener { 1288 1289 private Table m_edges; 1290 private Column m_scol, m_tcol; 1291 private int m_sidx, m_tidx; 1292 1293 public void setEdgeTable(Table edges) { 1294 if ( m_scol != null ) m_scol.removeColumnListener(this); 1296 if ( m_tcol != null ) m_tcol.removeColumnListener(this); 1297 m_scol = m_tcol = null; 1298 m_sidx = m_tidx = -1; 1299 1300 m_edges = edges; 1301 1302 if ( m_edges != null ) { 1304 m_sidx = edges.getColumnNumber(m_skey); 1305 m_tidx = edges.getColumnNumber(m_tkey); 1306 m_scol = edges.getColumn(m_sidx); 1307 m_tcol = edges.getColumn(m_tidx); 1308 m_scol.addColumnListener(this); 1309 m_tcol.addColumnListener(this); 1310 } 1311 } 1312 1313 public void tableChanged(Table t, int start, int end, int col, int type) { 1314 if ( !containsSet(t) ) 1315 throw new IllegalStateException ( 1316 "Graph shouldn't be listening to an unrelated table"); 1317 1318 if ( type != EventConstants.UPDATE ) { 1319 if ( t == getNodeTable() ) { 1320 if ( col == EventConstants.ALL_COLUMNS ) { 1322 boolean added = type==EventConstants.INSERT; 1323 for ( int r=start; r<=end; ++r ) 1324 updateNodeData(r, added); 1325 } 1326 } else { 1327 if ( col == EventConstants.ALL_COLUMNS ) { 1329 boolean added = type==EventConstants.INSERT; 1330 for ( int r=start; r<=end; ++r ) 1331 updateDegrees(start, added?1:-1); 1332 } 1333 } 1334 m_spanning = null; 1336 } 1337 fireGraphEvent(t, start, end, col, type); 1338 } 1339 1340 public void columnChanged(Column src, int idx, int prev) { 1341 columnChanged(src, idx, (long)prev); 1342 } 1343 1344 public void columnChanged(Column src, int idx, long prev) { 1345 if ( SRC==m_scol || SRC==m_tcol ) { 1346 boolean isSrc = SRC==m_scol; 1347 int e = m_edges.getTableRow(idx, isSrc?m_sidx:m_tidx); 1348 if ( e == -1 ) 1349 return; int s = getSourceNode(e); 1351 int t = getTargetNode(e); 1352 int p = getNodeIndex(prev); 1353 if ( p > -1 && ((isSrc && t > -1) || (!isSrc && s > -1)) ) 1354 updateDegrees(e, isSrc?p:s, isSrc?t:p, -1); 1355 if ( s > -1 && t > -1 ) 1356 updateDegrees(e, s, t, 1); 1357 } else { 1358 throw new IllegalStateException (); 1359 } 1360 } 1361 1362 public void columnChanged(Column src, int type, int start, int end) { 1363 throw new IllegalStateException (); 1365 } 1366 public void columnChanged(Column src, int idx, float prev) { 1367 throw new IllegalStateException (); 1369 } 1370 public void columnChanged(Column src, int idx, double prev) { 1371 throw new IllegalStateException (); 1373 } 1374 public void columnChanged(Column src, int idx, boolean prev) { 1375 throw new IllegalStateException (); 1377 } 1378 public void columnChanged(Column src, int idx, Object prev) { 1379 throw new IllegalStateException (); 1381 } 1382 } 1384 1385 1388 1389 protected static final String INDEGREE = "_indegree"; 1390 1391 protected static final String OUTDEGREE = "_outdegree"; 1392 1393 protected static final String INLINKS = "_inlinks"; 1394 1395 protected static final String OUTLINKS = "_outlinks"; 1396 1397 protected static final Schema LINKS_SCHEMA = new Schema(); 1398 static { 1399 Integer defaultValue = new Integer (0); 1400 LINKS_SCHEMA.addColumn(INDEGREE, int.class, defaultValue); 1401 LINKS_SCHEMA.addColumn(OUTDEGREE, int.class, defaultValue); 1402 LINKS_SCHEMA.addColumn(INLINKS, int[].class); 1403 LINKS_SCHEMA.addColumn(OUTLINKS, int[].class); 1404 LINKS_SCHEMA.lockSchema(); 1405 } 1406 1407} | Popular Tags |