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.graph.GraphConstants.GRAPH_STRING_FORMAT;
020
021import com.google.common.annotations.Beta;
022import com.google.common.base.Function;
023import com.google.common.collect.ImmutableSet;
024import com.google.common.collect.Iterators;
025import com.google.common.collect.Maps;
026import com.google.common.collect.Sets;
027import com.google.common.math.IntMath;
028import java.util.AbstractSet;
029import java.util.Iterator;
030import java.util.Map;
031import java.util.Set;
032import javax.annotation.Nullable;
033
034/**
035 * This class provides a skeletal implementation of {@link Network}. It is recommended to extend
036 * this class rather than implement {@link Network} directly.
037 *
038 * @author James Sexton
039 * @param <N> Node parameter type
040 * @param <E> Edge parameter type
041 * @since 20.0
042 */
043@Beta
044public abstract class AbstractNetwork<N, E> implements Network<N, E> {
045
046  @Override
047  public Graph<N> asGraph() {
048    return new AbstractGraph<N>() {
049      @Override
050      public Set<N> nodes() {
051        return AbstractNetwork.this.nodes();
052      }
053
054      @Override
055      public Set<EndpointPair<N>> edges() {
056        if (allowsParallelEdges()) {
057          return super.edges(); // Defer to AbstractGraph implementation.
058        }
059
060        // Optimized implementation assumes no parallel edges (1:1 edge to EndpointPair mapping).
061        return new AbstractSet<EndpointPair<N>>() {
062          @Override
063          public Iterator<EndpointPair<N>> iterator() {
064            return Iterators.transform(
065                AbstractNetwork.this.edges().iterator(),
066                new Function<E, EndpointPair<N>>() {
067                  @Override
068                  public EndpointPair<N> apply(E edge) {
069                    return incidentNodes(edge);
070                  }
071                });
072          }
073
074          @Override
075          public int size() {
076            return AbstractNetwork.this.edges().size();
077          }
078
079          // Mostly safe: We check contains(u) before calling successors(u), so we perform unsafe
080          // operations only in weird cases like checking for an EndpointPair<ArrayList> in a
081          // Network<LinkedList>.
082          @SuppressWarnings("unchecked")
083          @Override
084          public boolean contains(@Nullable Object obj) {
085            if (!(obj instanceof EndpointPair)) {
086              return false;
087            }
088            EndpointPair<?> endpointPair = (EndpointPair<?>) obj;
089            return isDirected() == endpointPair.isOrdered()
090                && nodes().contains(endpointPair.nodeU())
091                && successors((N) endpointPair.nodeU()).contains(endpointPair.nodeV());
092          }
093        };
094      }
095
096      @Override
097      public ElementOrder<N> nodeOrder() {
098        return AbstractNetwork.this.nodeOrder();
099      }
100
101      @Override
102      public boolean isDirected() {
103        return AbstractNetwork.this.isDirected();
104      }
105
106      @Override
107      public boolean allowsSelfLoops() {
108        return AbstractNetwork.this.allowsSelfLoops();
109      }
110
111      @Override
112      public Set<N> adjacentNodes(N node) {
113        return AbstractNetwork.this.adjacentNodes(node);
114      }
115
116      @Override
117      public Set<N> predecessors(N node) {
118        return AbstractNetwork.this.predecessors(node);
119      }
120
121      @Override
122      public Set<N> successors(N node) {
123        return AbstractNetwork.this.successors(node);
124      }
125
126      // DO NOT override the AbstractGraph *degree() implementations.
127    };
128  }
129
130  @Override
131  public int degree(N node) {
132    if (isDirected()) {
133      return IntMath.saturatedAdd(inEdges(node).size(), outEdges(node).size());
134    } else {
135      return IntMath.saturatedAdd(incidentEdges(node).size(), edgesConnecting(node, node).size());
136    }
137  }
138
139  @Override
140  public int inDegree(N node) {
141    return isDirected() ? inEdges(node).size() : degree(node);
142  }
143
144  @Override
145  public int outDegree(N node) {
146    return isDirected() ? outEdges(node).size() : degree(node);
147  }
148
149  @Override
150  public Set<E> adjacentEdges(E edge) {
151    EndpointPair<N> endpointPair = incidentNodes(edge); // Verifies that edge is in this network.
152    Set<E> endpointPairIncidentEdges =
153        Sets.union(incidentEdges(endpointPair.nodeU()), incidentEdges(endpointPair.nodeV()));
154    return Sets.difference(endpointPairIncidentEdges, ImmutableSet.of(edge));
155  }
156
157  @Override
158  public final boolean equals(@Nullable Object obj) {
159    if (obj == this) {
160      return true;
161    }
162    if (!(obj instanceof Network)) {
163      return false;
164    }
165    Network<?, ?> other = (Network<?, ?>) obj;
166
167    return isDirected() == other.isDirected()
168        && nodes().equals(other.nodes())
169        && edgeIncidentNodesMap(this).equals(edgeIncidentNodesMap(other));
170  }
171
172  @Override
173  public final int hashCode() {
174    return edgeIncidentNodesMap(this).hashCode();
175  }
176
177  /** Returns a string representation of this network. */
178  @Override
179  public String toString() {
180    String propertiesString =
181        String.format(
182            "isDirected: %s, allowsParallelEdges: %s, allowsSelfLoops: %s",
183            isDirected(), allowsParallelEdges(), allowsSelfLoops());
184    return String.format(
185        GRAPH_STRING_FORMAT, propertiesString, nodes(), edgeIncidentNodesMap(this));
186  }
187
188  private static <N, E> Map<E, EndpointPair<N>> edgeIncidentNodesMap(final Network<N, E> network) {
189    Function<E, EndpointPair<N>> edgeToIncidentNodesFn =
190        new Function<E, EndpointPair<N>>() {
191          @Override
192          public EndpointPair<N> apply(E edge) {
193            return network.incidentNodes(edge);
194          }
195        };
196    return Maps.asMap(network.edges(), edgeToIncidentNodesFn);
197  }
198}