001/*
002 * Copyright (C) 2016 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.google.common.graph;
018
019import static com.google.common.base.Preconditions.checkArgument;
020import static com.google.common.base.Preconditions.checkNotNull;
021import static com.google.common.collect.Iterators.transform;
022import static com.google.common.collect.Maps.asMap;
023import static com.google.common.collect.Sets.difference;
024import static com.google.common.collect.Sets.filter;
025import static com.google.common.collect.Sets.union;
026import static com.google.common.graph.GraphConstants.EDGE_REMOVED_FROM_GRAPH;
027import static com.google.common.graph.GraphConstants.ENDPOINTS_MISMATCH;
028import static com.google.common.graph.GraphConstants.MULTIPLE_EDGES_CONNECTING;
029import static com.google.common.graph.GraphConstants.NODE_PAIR_REMOVED_FROM_GRAPH;
030import static com.google.common.graph.GraphConstants.NODE_REMOVED_FROM_GRAPH;
031import static java.util.Collections.unmodifiableSet;
032
033import com.google.common.base.Predicate;
034import com.google.common.collect.ImmutableSet;
035import com.google.common.math.IntMath;
036import java.util.AbstractSet;
037import java.util.Iterator;
038import java.util.Map;
039import java.util.Optional;
040import java.util.Set;
041import org.jspecify.annotations.Nullable;
042
043/**
044 * This class provides a skeletal implementation of {@link Network}. It is recommended to extend
045 * this class rather than implement {@link Network} directly.
046 *
047 * <p>The methods implemented in this class should not be overridden unless the subclass admits a
048 * more efficient implementation.
049 *
050 * @author James Sexton
051 * @param <N> Node parameter type
052 * @param <E> Edge parameter type
053 * @since 20.0
054 */
055public abstract class AbstractNetwork<N, E> implements Network<N, E> {
056  /** Constructor for use by subclasses. */
057  protected AbstractNetwork() {}
058
059  @Override
060  public Graph<N> asGraph() {
061    Network<N, E> network = this;
062    return new AbstractGraph<N>() {
063      @Override
064      public Set<N> nodes() {
065        return network.nodes();
066      }
067
068      @Override
069      public Set<EndpointPair<N>> edges() {
070        if (allowsParallelEdges()) {
071          return super.edges(); // Defer to AbstractGraph implementation.
072        }
073
074        // Optimized implementation assumes no parallel edges (1:1 edge to EndpointPair mapping).
075        return new AbstractSet<EndpointPair<N>>() {
076          @Override
077          public Iterator<EndpointPair<N>> iterator() {
078            return transform(network.edges().iterator(), network::incidentNodes);
079          }
080
081          @Override
082          public int size() {
083            return network.edges().size();
084          }
085
086          // Mostly safe: We check contains(u) before calling successors(u), so we perform unsafe
087          // operations only in weird cases like checking for an EndpointPair<ArrayList> in a
088          // Network<LinkedList>.
089          @SuppressWarnings("unchecked")
090          @Override
091          public boolean contains(@Nullable Object obj) {
092            if (!(obj instanceof EndpointPair)) {
093              return false;
094            }
095            EndpointPair<?> endpointPair = (EndpointPair<?>) obj;
096            return isOrderingCompatible(endpointPair)
097                && nodes().contains(endpointPair.nodeU())
098                && successors((N) endpointPair.nodeU()).contains(endpointPair.nodeV());
099          }
100        };
101      }
102
103      @Override
104      public ElementOrder<N> nodeOrder() {
105        return network.nodeOrder();
106      }
107
108      @Override
109      public ElementOrder<N> incidentEdgeOrder() {
110        // TODO(b/142723300): Return network.incidentEdgeOrder() once Network has that method.
111        return ElementOrder.unordered();
112      }
113
114      @Override
115      public boolean isDirected() {
116        return network.isDirected();
117      }
118
119      @Override
120      public boolean allowsSelfLoops() {
121        return network.allowsSelfLoops();
122      }
123
124      @Override
125      public Set<N> adjacentNodes(N node) {
126        return network.adjacentNodes(node);
127      }
128
129      @Override
130      public Set<N> predecessors(N node) {
131        return network.predecessors(node);
132      }
133
134      @Override
135      public Set<N> successors(N node) {
136        return network.successors(node);
137      }
138
139      // DO NOT override the AbstractGraph *degree() implementations.
140    };
141  }
142
143  @Override
144  public int degree(N node) {
145    if (isDirected()) {
146      return IntMath.saturatedAdd(inEdges(node).size(), outEdges(node).size());
147    } else {
148      return IntMath.saturatedAdd(incidentEdges(node).size(), edgesConnecting(node, node).size());
149    }
150  }
151
152  @Override
153  public int inDegree(N node) {
154    return isDirected() ? inEdges(node).size() : degree(node);
155  }
156
157  @Override
158  public int outDegree(N node) {
159    return isDirected() ? outEdges(node).size() : degree(node);
160  }
161
162  @Override
163  public Set<E> adjacentEdges(E edge) {
164    EndpointPair<N> endpointPair = incidentNodes(edge); // Verifies that edge is in this network.
165    Set<E> endpointPairIncidentEdges =
166        union(incidentEdges(endpointPair.nodeU()), incidentEdges(endpointPair.nodeV()));
167    return edgeInvalidatableSet(difference(endpointPairIncidentEdges, ImmutableSet.of(edge)), edge);
168  }
169
170  @Override
171  public Set<E> edgesConnecting(N nodeU, N nodeV) {
172    checkNotNull(nodeU);
173    checkNotNull(nodeV);
174    Set<E> outEdgesU = outEdges(nodeU);
175    Set<E> inEdgesV = inEdges(nodeV);
176    return nodePairInvalidatableSet(
177        outEdgesU.size() <= inEdgesV.size()
178            ? unmodifiableSet(filter(outEdgesU, connectedPredicate(nodeU, nodeV)))
179            : unmodifiableSet(filter(inEdgesV, connectedPredicate(nodeV, nodeU))),
180        nodeU,
181        nodeV);
182  }
183
184  @Override
185  public Set<E> edgesConnecting(EndpointPair<N> endpoints) {
186    validateEndpoints(endpoints);
187    return edgesConnecting(endpoints.nodeU(), endpoints.nodeV());
188  }
189
190  private Predicate<E> connectedPredicate(N nodePresent, N nodeToCheck) {
191    return edge -> incidentNodes(edge).adjacentNode(nodePresent).equals(nodeToCheck);
192  }
193
194  @Override
195  public Optional<E> edgeConnecting(N nodeU, N nodeV) {
196    return Optional.ofNullable(edgeConnectingOrNull(nodeU, nodeV));
197  }
198
199  @Override
200  public Optional<E> edgeConnecting(EndpointPair<N> endpoints) {
201    validateEndpoints(endpoints);
202    return edgeConnecting(endpoints.nodeU(), endpoints.nodeV());
203  }
204
205  @Override
206  public @Nullable E edgeConnectingOrNull(N nodeU, N nodeV) {
207    Set<E> edgesConnecting = edgesConnecting(nodeU, nodeV);
208    switch (edgesConnecting.size()) {
209      case 0:
210        return null;
211      case 1:
212        return edgesConnecting.iterator().next();
213      default:
214        throw new IllegalArgumentException(String.format(MULTIPLE_EDGES_CONNECTING, nodeU, nodeV));
215    }
216  }
217
218  @Override
219  public @Nullable E edgeConnectingOrNull(EndpointPair<N> endpoints) {
220    validateEndpoints(endpoints);
221    return edgeConnectingOrNull(endpoints.nodeU(), endpoints.nodeV());
222  }
223
224  @Override
225  public boolean hasEdgeConnecting(N nodeU, N nodeV) {
226    checkNotNull(nodeU);
227    checkNotNull(nodeV);
228    return nodes().contains(nodeU) && successors(nodeU).contains(nodeV);
229  }
230
231  @Override
232  public boolean hasEdgeConnecting(EndpointPair<N> endpoints) {
233    checkNotNull(endpoints);
234    if (!isOrderingCompatible(endpoints)) {
235      return false;
236    }
237    return hasEdgeConnecting(endpoints.nodeU(), endpoints.nodeV());
238  }
239
240  /**
241   * Throws an IllegalArgumentException if the ordering of {@code endpoints} is not compatible with
242   * the directionality of this graph.
243   */
244  protected final void validateEndpoints(EndpointPair<?> endpoints) {
245    checkNotNull(endpoints);
246    checkArgument(isOrderingCompatible(endpoints), ENDPOINTS_MISMATCH);
247  }
248
249  protected final boolean isOrderingCompatible(EndpointPair<?> endpoints) {
250    return endpoints.isOrdered() == this.isDirected();
251  }
252
253  @Override
254  public final boolean equals(@Nullable Object obj) {
255    if (obj == this) {
256      return true;
257    }
258    if (!(obj instanceof Network)) {
259      return false;
260    }
261    Network<?, ?> other = (Network<?, ?>) obj;
262
263    return isDirected() == other.isDirected()
264        && nodes().equals(other.nodes())
265        && edgeIncidentNodesMap(this).equals(edgeIncidentNodesMap(other));
266  }
267
268  @Override
269  public final int hashCode() {
270    return edgeIncidentNodesMap(this).hashCode();
271  }
272
273  /** Returns a string representation of this network. */
274  @Override
275  public String toString() {
276    return "isDirected: "
277        + isDirected()
278        + ", allowsParallelEdges: "
279        + allowsParallelEdges()
280        + ", allowsSelfLoops: "
281        + allowsSelfLoops()
282        + ", nodes: "
283        + nodes()
284        + ", edges: "
285        + edgeIncidentNodesMap(this);
286  }
287
288  /**
289   * Returns a {@link Set} whose methods throw {@link IllegalStateException} when the given edge is
290   * not present in this network.
291   *
292   * @since 33.1.0
293   */
294  protected final <T> Set<T> edgeInvalidatableSet(Set<T> set, E edge) {
295    checkNotNull(edge);
296    return InvalidatableSet.of(
297        set, () -> edges().contains(edge), () -> String.format(EDGE_REMOVED_FROM_GRAPH, edge));
298  }
299
300  /**
301   * Returns a {@link Set} whose methods throw {@link IllegalStateException} when the given node is
302   * not present in this network.
303   *
304   * @since 33.1.0
305   */
306  protected final <T> Set<T> nodeInvalidatableSet(Set<T> set, N node) {
307    checkNotNull(node);
308    return InvalidatableSet.of(
309        set, () -> nodes().contains(node), () -> String.format(NODE_REMOVED_FROM_GRAPH, node));
310  }
311
312  /**
313   * Returns a {@link Set} whose methods throw {@link IllegalStateException} when either of the
314   * given nodes is not present in this network.
315   *
316   * @since 33.1.0
317   */
318  protected final <T> Set<T> nodePairInvalidatableSet(Set<T> set, N nodeU, N nodeV) {
319    checkNotNull(nodeU);
320    checkNotNull(nodeV);
321    return InvalidatableSet.of(
322        set,
323        () -> nodes().contains(nodeU) && nodes().contains(nodeV),
324        () -> String.format(NODE_PAIR_REMOVED_FROM_GRAPH, nodeU, nodeV));
325  }
326
327  private static <N, E> Map<E, EndpointPair<N>> edgeIncidentNodesMap(Network<N, E> network) {
328    return asMap(network.edges(), network::incidentNodes);
329  }
330}