• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.graph.GraphConstants.ENDPOINTS_MISMATCH;
22 import static com.google.common.graph.GraphConstants.MULTIPLE_EDGES_CONNECTING;
23 import static java.util.Collections.unmodifiableSet;
24 
25 import com.google.common.annotations.Beta;
26 import com.google.common.base.Function;
27 import com.google.common.base.Predicate;
28 import com.google.common.collect.ImmutableSet;
29 import com.google.common.collect.Iterators;
30 import com.google.common.collect.Maps;
31 import com.google.common.collect.Sets;
32 import com.google.common.math.IntMath;
33 import java.util.AbstractSet;
34 import java.util.Iterator;
35 import java.util.Map;
36 import java.util.Set;
37 import javax.annotation.CheckForNull;
38 
39 /**
40  * This class provides a skeletal implementation of {@link Network}. It is recommended to extend
41  * this class rather than implement {@link Network} directly.
42  *
43  * <p>The methods implemented in this class should not be overridden unless the subclass admits a
44  * more efficient implementation.
45  *
46  * @author James Sexton
47  * @param <N> Node parameter type
48  * @param <E> Edge parameter type
49  * @since 20.0
50  */
51 @Beta
52 @ElementTypesAreNonnullByDefault
53 public abstract class AbstractNetwork<N, E> implements Network<N, E> {
54 
55   @Override
asGraph()56   public Graph<N> asGraph() {
57     return new AbstractGraph<N>() {
58       @Override
59       public Set<N> nodes() {
60         return AbstractNetwork.this.nodes();
61       }
62 
63       @Override
64       public Set<EndpointPair<N>> edges() {
65         if (allowsParallelEdges()) {
66           return super.edges(); // Defer to AbstractGraph implementation.
67         }
68 
69         // Optimized implementation assumes no parallel edges (1:1 edge to EndpointPair mapping).
70         return new AbstractSet<EndpointPair<N>>() {
71           @Override
72           public Iterator<EndpointPair<N>> iterator() {
73             return Iterators.transform(
74                 AbstractNetwork.this.edges().iterator(),
75                 new Function<E, EndpointPair<N>>() {
76                   @Override
77                   public EndpointPair<N> apply(E edge) {
78                     return incidentNodes(edge);
79                   }
80                 });
81           }
82 
83           @Override
84           public int size() {
85             return AbstractNetwork.this.edges().size();
86           }
87 
88           // Mostly safe: We check contains(u) before calling successors(u), so we perform unsafe
89           // operations only in weird cases like checking for an EndpointPair<ArrayList> in a
90           // Network<LinkedList>.
91           @SuppressWarnings("unchecked")
92           @Override
93           public boolean contains(@CheckForNull Object obj) {
94             if (!(obj instanceof EndpointPair)) {
95               return false;
96             }
97             EndpointPair<?> endpointPair = (EndpointPair<?>) obj;
98             return isOrderingCompatible(endpointPair)
99                 && nodes().contains(endpointPair.nodeU())
100                 && successors((N) endpointPair.nodeU()).contains(endpointPair.nodeV());
101           }
102         };
103       }
104 
105       @Override
106       public ElementOrder<N> nodeOrder() {
107         return AbstractNetwork.this.nodeOrder();
108       }
109 
110       @Override
111       public ElementOrder<N> incidentEdgeOrder() {
112         // TODO(b/142723300): Return AbstractNetwork.this.incidentEdgeOrder() once Network has that
113         //   method.
114         return ElementOrder.unordered();
115       }
116 
117       @Override
118       public boolean isDirected() {
119         return AbstractNetwork.this.isDirected();
120       }
121 
122       @Override
123       public boolean allowsSelfLoops() {
124         return AbstractNetwork.this.allowsSelfLoops();
125       }
126 
127       @Override
128       public Set<N> adjacentNodes(N node) {
129         return AbstractNetwork.this.adjacentNodes(node);
130       }
131 
132       @Override
133       public Set<N> predecessors(N node) {
134         return AbstractNetwork.this.predecessors(node);
135       }
136 
137       @Override
138       public Set<N> successors(N node) {
139         return AbstractNetwork.this.successors(node);
140       }
141 
142       // DO NOT override the AbstractGraph *degree() implementations.
143     };
144   }
145 
146   @Override
147   public int degree(N node) {
148     if (isDirected()) {
149       return IntMath.saturatedAdd(inEdges(node).size(), outEdges(node).size());
150     } else {
151       return IntMath.saturatedAdd(incidentEdges(node).size(), edgesConnecting(node, node).size());
152     }
153   }
154 
155   @Override
156   public int inDegree(N node) {
157     return isDirected() ? inEdges(node).size() : degree(node);
158   }
159 
160   @Override
161   public int outDegree(N node) {
162     return isDirected() ? outEdges(node).size() : degree(node);
163   }
164 
165   @Override
166   public Set<E> adjacentEdges(E edge) {
167     EndpointPair<N> endpointPair = incidentNodes(edge); // Verifies that edge is in this network.
168     Set<E> endpointPairIncidentEdges =
169         Sets.union(incidentEdges(endpointPair.nodeU()), incidentEdges(endpointPair.nodeV()));
170     return Sets.difference(endpointPairIncidentEdges, ImmutableSet.of(edge));
171   }
172 
173   @Override
174   public Set<E> edgesConnecting(N nodeU, N nodeV) {
175     Set<E> outEdgesU = outEdges(nodeU);
176     Set<E> inEdgesV = inEdges(nodeV);
177     return outEdgesU.size() <= inEdgesV.size()
178         ? unmodifiableSet(Sets.filter(outEdgesU, connectedPredicate(nodeU, nodeV)))
179         : unmodifiableSet(Sets.filter(inEdgesV, connectedPredicate(nodeV, nodeU)));
180   }
181 
182   @Override
183   public Set<E> edgesConnecting(EndpointPair<N> endpoints) {
184     validateEndpoints(endpoints);
185     return edgesConnecting(endpoints.nodeU(), endpoints.nodeV());
186   }
187 
188   private Predicate<E> connectedPredicate(final N nodePresent, final N nodeToCheck) {
189     return new Predicate<E>() {
190       @Override
191       public boolean apply(E edge) {
192         return incidentNodes(edge).adjacentNode(nodePresent).equals(nodeToCheck);
193       }
194     };
195   }
196 
197   @Override
198   @CheckForNull
199   public E edgeConnectingOrNull(N nodeU, N nodeV) {
200     Set<E> edgesConnecting = edgesConnecting(nodeU, nodeV);
201     switch (edgesConnecting.size()) {
202       case 0:
203         return null;
204       case 1:
205         return edgesConnecting.iterator().next();
206       default:
207         throw new IllegalArgumentException(String.format(MULTIPLE_EDGES_CONNECTING, nodeU, nodeV));
208     }
209   }
210 
211   @Override
212   @CheckForNull
213   public E edgeConnectingOrNull(EndpointPair<N> endpoints) {
214     validateEndpoints(endpoints);
215     return edgeConnectingOrNull(endpoints.nodeU(), endpoints.nodeV());
216   }
217 
218   @Override
219   public boolean hasEdgeConnecting(N nodeU, N nodeV) {
220     checkNotNull(nodeU);
221     checkNotNull(nodeV);
222     return nodes().contains(nodeU) && successors(nodeU).contains(nodeV);
223   }
224 
225   @Override
226   public boolean hasEdgeConnecting(EndpointPair<N> endpoints) {
227     checkNotNull(endpoints);
228     if (!isOrderingCompatible(endpoints)) {
229       return false;
230     }
231     return hasEdgeConnecting(endpoints.nodeU(), endpoints.nodeV());
232   }
233 
234   /**
235    * Throws an IllegalArgumentException if the ordering of {@code endpoints} is not compatible with
236    * the directionality of this graph.
237    */
238   protected final void validateEndpoints(EndpointPair<?> endpoints) {
239     checkNotNull(endpoints);
240     checkArgument(isOrderingCompatible(endpoints), ENDPOINTS_MISMATCH);
241   }
242 
243   protected final boolean isOrderingCompatible(EndpointPair<?> endpoints) {
244     return endpoints.isOrdered() || !this.isDirected();
245   }
246 
247   @Override
248   public final boolean equals(@CheckForNull Object obj) {
249     if (obj == this) {
250       return true;
251     }
252     if (!(obj instanceof Network)) {
253       return false;
254     }
255     Network<?, ?> other = (Network<?, ?>) obj;
256 
257     return isDirected() == other.isDirected()
258         && nodes().equals(other.nodes())
259         && edgeIncidentNodesMap(this).equals(edgeIncidentNodesMap(other));
260   }
261 
262   @Override
263   public final int hashCode() {
264     return edgeIncidentNodesMap(this).hashCode();
265   }
266 
267   /** Returns a string representation of this network. */
268   @Override
269   public String toString() {
270     return "isDirected: "
271         + isDirected()
272         + ", allowsParallelEdges: "
273         + allowsParallelEdges()
274         + ", allowsSelfLoops: "
275         + allowsSelfLoops()
276         + ", nodes: "
277         + nodes()
278         + ", edges: "
279         + edgeIncidentNodesMap(this);
280   }
281 
282   private static <N, E> Map<E, EndpointPair<N>> edgeIncidentNodesMap(final Network<N, E> network) {
283     Function<E, EndpointPair<N>> edgeToIncidentNodesFn =
284         new Function<E, EndpointPair<N>>() {
285           @Override
286           public EndpointPair<N> apply(E edge) {
287             return network.incidentNodes(edge);
288           }
289         };
290     return Maps.asMap(network.edges(), edgeToIncidentNodesFn);
291   }
292 }
293