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 PredAndSucc(Object successorValue)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 NodeConnection(N node)76 NodeConnection(N node) { 77 this.node = checkNotNull(node); 78 } 79 80 static final class Pred<N> extends NodeConnection<N> { Pred(N node)81 Pred(N node) { 82 super(node); 83 } 84 85 @Override equals(@heckForNull Object that)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 hashCode()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> { Succ(N node)102 Succ(N node) { 103 super(node); 104 } 105 106 @Override equals(@heckForNull Object that)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 hashCode()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 DirectedGraphConnections( Map<N, Object> adjacentNodeValues, @CheckForNull List<NodeConnection<N>> orderedNodeConnections, int predecessorCount, int successorCount)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 of(ElementOrder<N> incidentEdgeOrder)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<>(); 167 break; 168 default: 169 throw new AssertionError(incidentEdgeOrder.type()); 170 } 171 172 return new DirectedGraphConnections<>( 173 /* adjacentNodeValues = */ new HashMap<N, Object>(initialCapacity, INNER_LOAD_FACTOR), 174 orderedNodeConnections, 175 /* predecessorCount = */ 0, 176 /* successorCount = */ 0); 177 } 178 ofImmutable( N thisNode, Iterable<EndpointPair<N>> incidentEdges, Function<N, V> successorNodeToValueFn)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 adjacentNodes()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 Iterator<NodeConnection<N>> nodeConnections = orderedNodeConnections.iterator(); 243 Set<N> seenNodes = new HashSet<>(); 244 return new AbstractIterator<N>() { 245 @Override 246 @CheckForNull 247 protected N computeNext() { 248 while (nodeConnections.hasNext()) { 249 NodeConnection<N> nodeConnection = nodeConnections.next(); 250 boolean added = seenNodes.add(nodeConnection.node); 251 if (added) { 252 return nodeConnection.node; 253 } 254 } 255 return endOfData(); 256 } 257 }; 258 } 259 260 @Override 261 public int size() { 262 return adjacentNodeValues.size(); 263 } 264 265 @Override 266 public boolean contains(@CheckForNull Object obj) { 267 return adjacentNodeValues.containsKey(obj); 268 } 269 }; 270 } 271 } 272 273 @Override 274 public Set<N> predecessors() { 275 return new AbstractSet<N>() { 276 @Override 277 public UnmodifiableIterator<N> iterator() { 278 if (orderedNodeConnections == null) { 279 Iterator<Entry<N, Object>> entries = adjacentNodeValues.entrySet().iterator(); 280 return new AbstractIterator<N>() { 281 @Override 282 @CheckForNull 283 protected N computeNext() { 284 while (entries.hasNext()) { 285 Entry<N, Object> entry = entries.next(); 286 if (isPredecessor(entry.getValue())) { 287 return entry.getKey(); 288 } 289 } 290 return endOfData(); 291 } 292 }; 293 } else { 294 Iterator<NodeConnection<N>> nodeConnections = orderedNodeConnections.iterator(); 295 return new AbstractIterator<N>() { 296 @Override 297 @CheckForNull 298 protected N computeNext() { 299 while (nodeConnections.hasNext()) { 300 NodeConnection<N> nodeConnection = nodeConnections.next(); 301 if (nodeConnection instanceof NodeConnection.Pred) { 302 return nodeConnection.node; 303 } 304 } 305 return endOfData(); 306 } 307 }; 308 } 309 } 310 311 @Override 312 public int size() { 313 return predecessorCount; 314 } 315 316 @Override 317 public boolean contains(@CheckForNull Object obj) { 318 return isPredecessor(adjacentNodeValues.get(obj)); 319 } 320 }; 321 } 322 323 @Override 324 public Set<N> successors() { 325 return new AbstractSet<N>() { 326 @Override 327 public UnmodifiableIterator<N> iterator() { 328 if (orderedNodeConnections == null) { 329 Iterator<Entry<N, Object>> entries = adjacentNodeValues.entrySet().iterator(); 330 return new AbstractIterator<N>() { 331 @Override 332 @CheckForNull 333 protected N computeNext() { 334 while (entries.hasNext()) { 335 Entry<N, Object> entry = entries.next(); 336 if (isSuccessor(entry.getValue())) { 337 return entry.getKey(); 338 } 339 } 340 return endOfData(); 341 } 342 }; 343 } else { 344 Iterator<NodeConnection<N>> nodeConnections = orderedNodeConnections.iterator(); 345 return new AbstractIterator<N>() { 346 @Override 347 @CheckForNull 348 protected N computeNext() { 349 while (nodeConnections.hasNext()) { 350 NodeConnection<N> nodeConnection = nodeConnections.next(); 351 if (nodeConnection instanceof NodeConnection.Succ) { 352 return nodeConnection.node; 353 } 354 } 355 return endOfData(); 356 } 357 }; 358 } 359 } 360 361 @Override 362 public int size() { 363 return successorCount; 364 } 365 366 @Override 367 public boolean contains(@CheckForNull Object obj) { 368 return isSuccessor(adjacentNodeValues.get(obj)); 369 } 370 }; 371 } 372 373 @Override 374 public Iterator<EndpointPair<N>> incidentEdgeIterator(N thisNode) { 375 checkNotNull(thisNode); 376 377 Iterator<EndpointPair<N>> resultWithDoubleSelfLoop; 378 if (orderedNodeConnections == null) { 379 resultWithDoubleSelfLoop = 380 Iterators.concat( 381 Iterators.transform( 382 predecessors().iterator(), 383 (N predecessor) -> EndpointPair.ordered(predecessor, thisNode)), 384 Iterators.transform( 385 successors().iterator(), 386 (N successor) -> EndpointPair.ordered(thisNode, successor))); 387 } else { 388 resultWithDoubleSelfLoop = 389 Iterators.transform( 390 orderedNodeConnections.iterator(), 391 (NodeConnection<N> connection) -> { 392 if (connection instanceof NodeConnection.Succ) { 393 return EndpointPair.ordered(thisNode, connection.node); 394 } else { 395 return EndpointPair.ordered(connection.node, thisNode); 396 } 397 }); 398 } 399 400 AtomicBoolean alreadySeenSelfLoop = new AtomicBoolean(false); 401 return new AbstractIterator<EndpointPair<N>>() { 402 @Override 403 @CheckForNull 404 protected EndpointPair<N> computeNext() { 405 while (resultWithDoubleSelfLoop.hasNext()) { 406 EndpointPair<N> edge = resultWithDoubleSelfLoop.next(); 407 if (edge.nodeU().equals(edge.nodeV())) { 408 if (!alreadySeenSelfLoop.getAndSet(true)) { 409 return edge; 410 } 411 } else { 412 return edge; 413 } 414 } 415 return endOfData(); 416 } 417 }; 418 } 419 420 @SuppressWarnings("unchecked") 421 @Override 422 @CheckForNull 423 public V value(N node) { 424 checkNotNull(node); 425 Object value = adjacentNodeValues.get(node); 426 if (value == PRED) { 427 return null; 428 } 429 if (value instanceof PredAndSucc) { 430 return (V) ((PredAndSucc) value).successorValue; 431 } 432 return (V) value; 433 } 434 435 @SuppressWarnings("unchecked") 436 @Override 437 public void removePredecessor(N node) { 438 checkNotNull(node); 439 440 Object previousValue = adjacentNodeValues.get(node); 441 boolean removedPredecessor; 442 443 if (previousValue == PRED) { 444 adjacentNodeValues.remove(node); 445 removedPredecessor = true; 446 } else if (previousValue instanceof PredAndSucc) { 447 adjacentNodeValues.put((N) node, ((PredAndSucc) previousValue).successorValue); 448 removedPredecessor = true; 449 } else { 450 removedPredecessor = false; 451 } 452 453 if (removedPredecessor) { 454 checkNonNegative(--predecessorCount); 455 456 if (orderedNodeConnections != null) { 457 orderedNodeConnections.remove(new NodeConnection.Pred<>(node)); 458 } 459 } 460 } 461 462 @SuppressWarnings("unchecked") 463 @Override 464 @CheckForNull 465 public V removeSuccessor(Object node) { 466 checkNotNull(node); 467 Object previousValue = adjacentNodeValues.get(node); 468 Object removedValue; 469 470 if (previousValue == null || previousValue == PRED) { 471 removedValue = null; 472 } else if (previousValue instanceof PredAndSucc) { 473 adjacentNodeValues.put((N) node, PRED); 474 removedValue = ((PredAndSucc) previousValue).successorValue; 475 } else { // successor 476 adjacentNodeValues.remove(node); 477 removedValue = previousValue; 478 } 479 480 if (removedValue != null) { 481 checkNonNegative(--successorCount); 482 483 if (orderedNodeConnections != null) { 484 orderedNodeConnections.remove(new NodeConnection.Succ<>((N) node)); 485 } 486 } 487 488 /* 489 * TODO(cpovirk): `return (V) removedValue` once our checker permits that. 490 * 491 * (We promoted a class of warnings into errors because sometimes they indicate real problems. 492 * But now we need to "undo" some instance of spurious errors, as discussed in 493 * https://github.com/jspecify/checker-framework/issues/8.) 494 */ 495 return removedValue == null ? null : (V) removedValue; 496 } 497 498 @Override 499 public void addPredecessor(N node, V unused) { 500 Object previousValue = adjacentNodeValues.put(node, PRED); 501 boolean addedPredecessor; 502 503 if (previousValue == null) { 504 addedPredecessor = true; 505 } else if (previousValue instanceof PredAndSucc) { 506 // Restore previous PredAndSucc object. 507 adjacentNodeValues.put(node, previousValue); 508 addedPredecessor = false; 509 } else if (previousValue != PRED) { // successor 510 // Do NOT use method parameter value 'unused'. In directed graphs, successors store the value. 511 adjacentNodeValues.put(node, new PredAndSucc(previousValue)); 512 addedPredecessor = true; 513 } else { 514 addedPredecessor = false; 515 } 516 517 if (addedPredecessor) { 518 checkPositive(++predecessorCount); 519 520 if (orderedNodeConnections != null) { 521 orderedNodeConnections.add(new NodeConnection.Pred<>(node)); 522 } 523 } 524 } 525 526 @SuppressWarnings("unchecked") 527 @Override 528 @CheckForNull 529 public V addSuccessor(N node, V value) { 530 Object previousValue = adjacentNodeValues.put(node, value); 531 Object previousSuccessor; 532 533 if (previousValue == null) { 534 previousSuccessor = null; 535 } else if (previousValue instanceof PredAndSucc) { 536 adjacentNodeValues.put(node, new PredAndSucc(value)); 537 previousSuccessor = ((PredAndSucc) previousValue).successorValue; 538 } else if (previousValue == PRED) { 539 adjacentNodeValues.put(node, new PredAndSucc(value)); 540 previousSuccessor = null; 541 } else { // successor 542 previousSuccessor = previousValue; 543 } 544 545 if (previousSuccessor == null) { 546 checkPositive(++successorCount); 547 548 if (orderedNodeConnections != null) { 549 orderedNodeConnections.add(new NodeConnection.Succ<>(node)); 550 } 551 } 552 553 // See the comment on the similar cast in removeSuccessor. 554 return previousSuccessor == null ? null : (V) previousSuccessor; 555 } 556 557 private static boolean isPredecessor(@CheckForNull Object value) { 558 return (value == PRED) || (value instanceof PredAndSucc); 559 } 560 561 private static boolean isSuccessor(@CheckForNull Object value) { 562 return (value != PRED) && (value != null); 563 } 564 } 565