Coverage Summary for Class: DirectedGraphConnections (com.google.common.graph)
| Class | Method, % | Line, % |
|---|---|---|
| DirectedGraphConnections | 0% (0/16) | 0% (0/134) |
| DirectedGraphConnections$1 | 0% (0/4) | 0% (0/6) |
| DirectedGraphConnections$1$1 | 0% (0/2) | 0% (0/8) |
| DirectedGraphConnections$2 | 0% (0/4) | 0% (0/8) |
| DirectedGraphConnections$2$1 | 0% (0/2) | 0% (0/7) |
| DirectedGraphConnections$2$2 | 0% (0/2) | 0% (0/7) |
| DirectedGraphConnections$3 | 0% (0/4) | 0% (0/8) |
| DirectedGraphConnections$3$1 | 0% (0/2) | 0% (0/7) |
| DirectedGraphConnections$3$2 | 0% (0/2) | 0% (0/7) |
| DirectedGraphConnections$4 | 0% (0/2) | 0% (0/2) |
| DirectedGraphConnections$5 | 0% (0/2) | 0% (0/2) |
| DirectedGraphConnections$6 | 0% (0/2) | 0% (0/4) |
| DirectedGraphConnections$7 | 0% (0/2) | 0% (0/9) |
| DirectedGraphConnections$8 | 0% (0/1) | 0% (0/1) |
| DirectedGraphConnections$NodeConnection | 0% (0/1) | 0% (0/2) |
| DirectedGraphConnections$NodeConnection$Pred | 0% (0/3) | 0% (0/5) |
| DirectedGraphConnections$NodeConnection$Succ | 0% (0/3) | 0% (0/5) |
| DirectedGraphConnections$PredAndSucc | 0% (0/2) | 0% (0/3) |
| Total | 0% (0/56) | 0% (0/225) |
1 /* 2 * Copyright (C) 2016 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.graph; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static com.google.common.base.Preconditions.checkState; 22 import static com.google.common.graph.GraphConstants.INNER_CAPACITY; 23 import static com.google.common.graph.GraphConstants.INNER_LOAD_FACTOR; 24 import static com.google.common.graph.Graphs.checkNonNegative; 25 import static com.google.common.graph.Graphs.checkPositive; 26 27 import com.google.common.base.Function; 28 import com.google.common.collect.AbstractIterator; 29 import com.google.common.collect.ImmutableList; 30 import com.google.common.collect.Iterators; 31 import com.google.common.collect.UnmodifiableIterator; 32 import java.util.AbstractSet; 33 import java.util.ArrayList; 34 import java.util.Collections; 35 import java.util.HashMap; 36 import java.util.HashSet; 37 import java.util.Iterator; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Map.Entry; 41 import java.util.Set; 42 import java.util.concurrent.atomic.AtomicBoolean; 43 import javax.annotation.CheckForNull; 44 45 /** 46 * An implementation of {@link GraphConnections} for directed graphs. 47 * 48 * @author James Sexton 49 * @author Jens Nyman 50 * @param <N> Node parameter type 51 * @param <V> Value parameter type 52 */ 53 @ElementTypesAreNonnullByDefault 54 final class DirectedGraphConnections<N, V> implements GraphConnections<N, V> { 55 /** 56 * A wrapper class to indicate a node is both a predecessor and successor while still providing 57 * the successor value. 58 */ 59 private static final class PredAndSucc { 60 private final Object successorValue; 61 62 PredAndSucc(Object successorValue) { 63 this.successorValue = successorValue; 64 } 65 } 66 67 /** 68 * A value class representing single connection between the origin node and another node. 69 * 70 * <p>There can be two types of connections (predecessor and successor), which is represented by 71 * the two implementations. 72 */ 73 private abstract static class NodeConnection<N> { 74 final N node; 75 76 NodeConnection(N node) { 77 this.node = checkNotNull(node); 78 } 79 80 static final class Pred<N> extends NodeConnection<N> { 81 Pred(N node) { 82 super(node); 83 } 84 85 @Override 86 public boolean equals(@CheckForNull Object that) { 87 if (that instanceof Pred) { 88 return this.node.equals(((Pred<?>) that).node); 89 } else { 90 return false; 91 } 92 } 93 94 @Override 95 public int hashCode() { 96 // Adding the class hashCode to avoid a clash with Succ instances. 97 return Pred.class.hashCode() + node.hashCode(); 98 } 99 } 100 101 static final class Succ<N> extends NodeConnection<N> { 102 Succ(N node) { 103 super(node); 104 } 105 106 @Override 107 public boolean equals(@CheckForNull Object that) { 108 if (that instanceof Succ) { 109 return this.node.equals(((Succ<?>) that).node); 110 } else { 111 return false; 112 } 113 } 114 115 @Override 116 public int hashCode() { 117 // Adding the class hashCode to avoid a clash with Pred instances. 118 return Succ.class.hashCode() + node.hashCode(); 119 } 120 } 121 } 122 123 private static final Object PRED = new Object(); 124 125 // Every value in this map must either be an instance of PredAndSucc with a successorValue of 126 // type V, PRED (representing predecessor), or an instance of type V (representing successor). 127 private final Map<N, Object> adjacentNodeValues; 128 129 /** 130 * All node connections in this graph, in edge insertion order. 131 * 132 * <p>Note: This field and {@link #adjacentNodeValues} cannot be combined into a single 133 * LinkedHashMap because one target node may be mapped to both a predecessor and a successor. A 134 * LinkedHashMap combines two such edges into a single node-value pair, even though the edges may 135 * not have been inserted consecutively. 136 */ 137 @CheckForNull private final List<NodeConnection<N>> orderedNodeConnections; 138 139 private int predecessorCount; 140 private int successorCount; 141 142 private DirectedGraphConnections( 143 Map<N, Object> adjacentNodeValues, 144 @CheckForNull List<NodeConnection<N>> orderedNodeConnections, 145 int predecessorCount, 146 int successorCount) { 147 this.adjacentNodeValues = checkNotNull(adjacentNodeValues); 148 this.orderedNodeConnections = orderedNodeConnections; 149 this.predecessorCount = checkNonNegative(predecessorCount); 150 this.successorCount = checkNonNegative(successorCount); 151 checkState( 152 predecessorCount <= adjacentNodeValues.size() 153 && successorCount <= adjacentNodeValues.size()); 154 } 155 156 static <N, V> DirectedGraphConnections<N, V> of(ElementOrder<N> incidentEdgeOrder) { 157 // We store predecessors and successors in the same map, so double the initial capacity. 158 int initialCapacity = INNER_CAPACITY * 2; 159 160 List<NodeConnection<N>> orderedNodeConnections; 161 switch (incidentEdgeOrder.type()) { 162 case UNORDERED: 163 orderedNodeConnections = null; 164 break; 165 case STABLE: 166 orderedNodeConnections = new ArrayList<NodeConnection<N>>(); 167 break; 168 default: 169 throw new AssertionError(incidentEdgeOrder.type()); 170 } 171 172 return new DirectedGraphConnections<N, V>( 173 /* adjacentNodeValues = */ new HashMap<N, Object>(initialCapacity, INNER_LOAD_FACTOR), 174 orderedNodeConnections, 175 /* predecessorCount = */ 0, 176 /* successorCount = */ 0); 177 } 178 179 static <N, V> DirectedGraphConnections<N, V> ofImmutable( 180 N thisNode, Iterable<EndpointPair<N>> incidentEdges, Function<N, V> successorNodeToValueFn) { 181 checkNotNull(thisNode); 182 checkNotNull(successorNodeToValueFn); 183 184 Map<N, Object> adjacentNodeValues = new HashMap<>(); 185 ImmutableList.Builder<NodeConnection<N>> orderedNodeConnectionsBuilder = 186 ImmutableList.builder(); 187 int predecessorCount = 0; 188 int successorCount = 0; 189 190 for (EndpointPair<N> incidentEdge : incidentEdges) { 191 if (incidentEdge.nodeU().equals(thisNode) && incidentEdge.nodeV().equals(thisNode)) { 192 // incidentEdge is a self-loop 193 194 adjacentNodeValues.put(thisNode, new PredAndSucc(successorNodeToValueFn.apply(thisNode))); 195 196 orderedNodeConnectionsBuilder.add(new NodeConnection.Pred<>(thisNode)); 197 orderedNodeConnectionsBuilder.add(new NodeConnection.Succ<>(thisNode)); 198 predecessorCount++; 199 successorCount++; 200 } else if (incidentEdge.nodeV().equals(thisNode)) { // incidentEdge is an inEdge 201 N predecessor = incidentEdge.nodeU(); 202 203 Object existingValue = adjacentNodeValues.put(predecessor, PRED); 204 if (existingValue != null) { 205 adjacentNodeValues.put(predecessor, new PredAndSucc(existingValue)); 206 } 207 208 orderedNodeConnectionsBuilder.add(new NodeConnection.Pred<>(predecessor)); 209 predecessorCount++; 210 } else { // incidentEdge is an outEdge 211 checkArgument(incidentEdge.nodeU().equals(thisNode)); 212 213 N successor = incidentEdge.nodeV(); 214 V value = successorNodeToValueFn.apply(successor); 215 216 Object existingValue = adjacentNodeValues.put(successor, value); 217 if (existingValue != null) { 218 checkArgument(existingValue == PRED); 219 adjacentNodeValues.put(successor, new PredAndSucc(value)); 220 } 221 222 orderedNodeConnectionsBuilder.add(new NodeConnection.Succ<>(successor)); 223 successorCount++; 224 } 225 } 226 227 return new DirectedGraphConnections<>( 228 adjacentNodeValues, 229 orderedNodeConnectionsBuilder.build(), 230 predecessorCount, 231 successorCount); 232 } 233 234 @Override 235 public Set<N> adjacentNodes() { 236 if (orderedNodeConnections == null) { 237 return Collections.unmodifiableSet(adjacentNodeValues.keySet()); 238 } else { 239 return new AbstractSet<N>() { 240 @Override 241 public UnmodifiableIterator<N> iterator() { 242 final Iterator<NodeConnection<N>> nodeConnections = orderedNodeConnections.iterator(); 243 final Set<N> seenNodes = new HashSet<>(); 244 return new AbstractIterator<N>() { 245 @Override 246 protected N computeNext() { 247 while (nodeConnections.hasNext()) { 248 NodeConnection<N> nodeConnection = nodeConnections.next(); 249 boolean added = seenNodes.add(nodeConnection.node); 250 if (added) { 251 return nodeConnection.node; 252 } 253 } 254 return endOfData(); 255 } 256 }; 257 } 258 259 @Override 260 public int size() { 261 return adjacentNodeValues.size(); 262 } 263 264 @Override 265 public boolean contains(@CheckForNull Object obj) { 266 return adjacentNodeValues.containsKey(obj); 267 } 268 }; 269 } 270 } 271 272 @Override 273 public Set<N> predecessors() { 274 return new AbstractSet<N>() { 275 @Override 276 public UnmodifiableIterator<N> iterator() { 277 if (orderedNodeConnections == null) { 278 final Iterator<Entry<N, Object>> entries = adjacentNodeValues.entrySet().iterator(); 279 return new AbstractIterator<N>() { 280 @Override 281 protected N computeNext() { 282 while (entries.hasNext()) { 283 Entry<N, Object> entry = entries.next(); 284 if (isPredecessor(entry.getValue())) { 285 return entry.getKey(); 286 } 287 } 288 return endOfData(); 289 } 290 }; 291 } else { 292 final Iterator<NodeConnection<N>> nodeConnections = orderedNodeConnections.iterator(); 293 return new AbstractIterator<N>() { 294 @Override 295 protected N computeNext() { 296 while (nodeConnections.hasNext()) { 297 NodeConnection<N> nodeConnection = nodeConnections.next(); 298 if (nodeConnection instanceof NodeConnection.Pred) { 299 return nodeConnection.node; 300 } 301 } 302 return endOfData(); 303 } 304 }; 305 } 306 } 307 308 @Override 309 public int size() { 310 return predecessorCount; 311 } 312 313 @Override 314 public boolean contains(@CheckForNull Object obj) { 315 return isPredecessor(adjacentNodeValues.get(obj)); 316 } 317 }; 318 } 319 320 @Override 321 public Set<N> successors() { 322 return new AbstractSet<N>() { 323 @Override 324 public UnmodifiableIterator<N> iterator() { 325 if (orderedNodeConnections == null) { 326 final Iterator<Entry<N, Object>> entries = adjacentNodeValues.entrySet().iterator(); 327 return new AbstractIterator<N>() { 328 @Override 329 protected N computeNext() { 330 while (entries.hasNext()) { 331 Entry<N, Object> entry = entries.next(); 332 if (isSuccessor(entry.getValue())) { 333 return entry.getKey(); 334 } 335 } 336 return endOfData(); 337 } 338 }; 339 } else { 340 final Iterator<NodeConnection<N>> nodeConnections = orderedNodeConnections.iterator(); 341 return new AbstractIterator<N>() { 342 @Override 343 protected N computeNext() { 344 while (nodeConnections.hasNext()) { 345 NodeConnection<N> nodeConnection = nodeConnections.next(); 346 if (nodeConnection instanceof NodeConnection.Succ) { 347 return nodeConnection.node; 348 } 349 } 350 return endOfData(); 351 } 352 }; 353 } 354 } 355 356 @Override 357 public int size() { 358 return successorCount; 359 } 360 361 @Override 362 public boolean contains(@CheckForNull Object obj) { 363 return isSuccessor(adjacentNodeValues.get(obj)); 364 } 365 }; 366 } 367 368 @Override 369 public Iterator<EndpointPair<N>> incidentEdgeIterator(final N thisNode) { 370 checkNotNull(thisNode); 371 372 final Iterator<EndpointPair<N>> resultWithDoubleSelfLoop; 373 if (orderedNodeConnections == null) { 374 resultWithDoubleSelfLoop = 375 Iterators.concat( 376 Iterators.transform( 377 predecessors().iterator(), 378 new Function<N, EndpointPair<N>>() { 379 @Override 380 public EndpointPair<N> apply(N predecessor) { 381 return EndpointPair.ordered(predecessor, thisNode); 382 } 383 }), 384 Iterators.transform( 385 successors().iterator(), 386 new Function<N, EndpointPair<N>>() { 387 @Override 388 public EndpointPair<N> apply(N successor) { 389 return EndpointPair.ordered(thisNode, successor); 390 } 391 })); 392 } else { 393 resultWithDoubleSelfLoop = 394 Iterators.transform( 395 orderedNodeConnections.iterator(), 396 new Function<NodeConnection<N>, EndpointPair<N>>() { 397 @Override 398 public EndpointPair<N> apply(NodeConnection<N> connection) { 399 if (connection instanceof NodeConnection.Succ) { 400 return EndpointPair.ordered(thisNode, connection.node); 401 } else { 402 return EndpointPair.ordered(connection.node, thisNode); 403 } 404 } 405 }); 406 } 407 408 final AtomicBoolean alreadySeenSelfLoop = new AtomicBoolean(false); 409 return new AbstractIterator<EndpointPair<N>>() { 410 @Override 411 protected EndpointPair<N> computeNext() { 412 while (resultWithDoubleSelfLoop.hasNext()) { 413 EndpointPair<N> edge = resultWithDoubleSelfLoop.next(); 414 if (edge.nodeU().equals(edge.nodeV())) { 415 if (!alreadySeenSelfLoop.getAndSet(true)) { 416 return edge; 417 } 418 } else { 419 return edge; 420 } 421 } 422 return endOfData(); 423 } 424 }; 425 } 426 427 @SuppressWarnings("unchecked") 428 @Override 429 @CheckForNull 430 public V value(N node) { 431 checkNotNull(node); 432 Object value = adjacentNodeValues.get(node); 433 if (value == PRED) { 434 return null; 435 } 436 if (value instanceof PredAndSucc) { 437 return (V) ((PredAndSucc) value).successorValue; 438 } 439 return (V) value; 440 } 441 442 @SuppressWarnings("unchecked") 443 @Override 444 public void removePredecessor(N node) { 445 checkNotNull(node); 446 447 Object previousValue = adjacentNodeValues.get(node); 448 boolean removedPredecessor; 449 450 if (previousValue == PRED) { 451 adjacentNodeValues.remove(node); 452 removedPredecessor = true; 453 } else if (previousValue instanceof PredAndSucc) { 454 adjacentNodeValues.put((N) node, ((PredAndSucc) previousValue).successorValue); 455 removedPredecessor = true; 456 } else { 457 removedPredecessor = false; 458 } 459 460 if (removedPredecessor) { 461 checkNonNegative(--predecessorCount); 462 463 if (orderedNodeConnections != null) { 464 orderedNodeConnections.remove(new NodeConnection.Pred<>(node)); 465 } 466 } 467 } 468 469 @SuppressWarnings("unchecked") 470 @Override 471 @CheckForNull 472 public V removeSuccessor(Object node) { 473 checkNotNull(node); 474 Object previousValue = adjacentNodeValues.get(node); 475 Object removedValue; 476 477 if (previousValue == null || previousValue == PRED) { 478 removedValue = null; 479 } else if (previousValue instanceof PredAndSucc) { 480 adjacentNodeValues.put((N) node, PRED); 481 removedValue = ((PredAndSucc) previousValue).successorValue; 482 } else { // successor 483 adjacentNodeValues.remove(node); 484 removedValue = previousValue; 485 } 486 487 if (removedValue != null) { 488 checkNonNegative(--successorCount); 489 490 if (orderedNodeConnections != null) { 491 orderedNodeConnections.remove(new NodeConnection.Succ<>((N) node)); 492 } 493 } 494 495 /* 496 * TODO(cpovirk): `return (V) removedValue` once our checker permits that. 497 * 498 * (We promoted a class of warnings into errors because sometimes they indicate real problems. 499 * But now we need to "undo" some instance of spurious errors, as discussed in 500 * https://github.com/jspecify/checker-framework/issues/8.) 501 */ 502 return removedValue == null ? null : (V) removedValue; 503 } 504 505 @Override 506 public void addPredecessor(N node, V unused) { 507 Object previousValue = adjacentNodeValues.put(node, PRED); 508 boolean addedPredecessor; 509 510 if (previousValue == null) { 511 addedPredecessor = true; 512 } else if (previousValue instanceof PredAndSucc) { 513 // Restore previous PredAndSucc object. 514 adjacentNodeValues.put(node, previousValue); 515 addedPredecessor = false; 516 } else if (previousValue != PRED) { // successor 517 // Do NOT use method parameter value 'unused'. In directed graphs, successors store the value. 518 adjacentNodeValues.put(node, new PredAndSucc(previousValue)); 519 addedPredecessor = true; 520 } else { 521 addedPredecessor = false; 522 } 523 524 if (addedPredecessor) { 525 checkPositive(++predecessorCount); 526 527 if (orderedNodeConnections != null) { 528 orderedNodeConnections.add(new NodeConnection.Pred<>(node)); 529 } 530 } 531 } 532 533 @SuppressWarnings("unchecked") 534 @Override 535 @CheckForNull 536 public V addSuccessor(N node, V value) { 537 Object previousValue = adjacentNodeValues.put(node, value); 538 Object previousSuccessor; 539 540 if (previousValue == null) { 541 previousSuccessor = null; 542 } else if (previousValue instanceof PredAndSucc) { 543 adjacentNodeValues.put(node, new PredAndSucc(value)); 544 previousSuccessor = ((PredAndSucc) previousValue).successorValue; 545 } else if (previousValue == PRED) { 546 adjacentNodeValues.put(node, new PredAndSucc(value)); 547 previousSuccessor = null; 548 } else { // successor 549 previousSuccessor = previousValue; 550 } 551 552 if (previousSuccessor == null) { 553 checkPositive(++successorCount); 554 555 if (orderedNodeConnections != null) { 556 orderedNodeConnections.add(new NodeConnection.Succ<>(node)); 557 } 558 } 559 560 // See the comment on the similar cast in removeSuccessor. 561 return previousSuccessor == null ? null : (V) previousSuccessor; 562 } 563 564 private static boolean isPredecessor(@CheckForNull Object value) { 565 return (value == PRED) || (value instanceof PredAndSucc); 566 } 567 568 private static boolean isSuccessor(@CheckForNull Object value) { 569 return (value != PRED) && (value != null); 570 } 571 }