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.checkNotNull;
020import static com.google.common.graph.GraphConstants.NOT_AVAILABLE_ON_UNDIRECTED;
021
022import com.google.common.annotations.Beta;
023import com.google.common.base.Objects;
024import com.google.common.collect.Iterators;
025import com.google.common.collect.UnmodifiableIterator;
026import javax.annotation.Nullable;
027
028/**
029 * An immutable pair representing the two (possibly equal, in the case of a self-loop) endpoints of
030 * an edge in a graph. The {@link EndpointPair} of a directed edge is an ordered pair of nodes
031 * ({@link #source()} and {@link #target()}). The {@link EndpointPair} of an undirected edge is an
032 * unordered pair of nodes ({@link #nodeU()} and {@link #nodeV()}).
033 *
034 * @author James Sexton
035 * @since 20.0
036 */
037@Beta
038public abstract class EndpointPair<N> implements Iterable<N> {
039  private final N nodeU;
040  private final N nodeV;
041
042  private EndpointPair(N nodeU, N nodeV) {
043    this.nodeU = checkNotNull(nodeU);
044    this.nodeV = checkNotNull(nodeV);
045  }
046
047  /** Returns an {@link EndpointPair} representing the endpoints of a directed edge. */
048  public static <N> EndpointPair<N> ordered(N source, N target) {
049    return new Ordered<N>(source, target);
050  }
051
052  /** Returns an {@link EndpointPair} representing the endpoints of an undirected edge. */
053  public static <N> EndpointPair<N> unordered(N nodeU, N nodeV) {
054    // Swap nodes on purpose to prevent callers from relying on the "ordering" of an unordered pair.
055    return new Unordered<N>(nodeV, nodeU);
056  }
057
058  /** Returns an {@link EndpointPair} representing the endpoints of an edge in {@code graph}. */
059  static <N> EndpointPair<N> of(Graph<?> graph, N nodeU, N nodeV) {
060    return graph.isDirected() ? ordered(nodeU, nodeV) : unordered(nodeU, nodeV);
061  }
062
063  /** Returns an {@link EndpointPair} representing the endpoints of an edge in {@code network}. */
064  static <N> EndpointPair<N> of(Network<?, ?> network, N nodeU, N nodeV) {
065    return network.isDirected() ? ordered(nodeU, nodeV) : unordered(nodeU, nodeV);
066  }
067
068  /**
069   * If this {@link EndpointPair} {@link #isOrdered()}, returns the node which is the source.
070   *
071   * @throws UnsupportedOperationException if this {@link EndpointPair} is not ordered
072   */
073  public abstract N source();
074
075  /**
076   * If this {@link EndpointPair} {@link #isOrdered()}, returns the node which is the target.
077   *
078   * @throws UnsupportedOperationException if this {@link EndpointPair} is not ordered
079   */
080  public abstract N target();
081
082  /**
083   * If this {@link EndpointPair} {@link #isOrdered()} returns the {@link #source()}; otherwise,
084   * returns an arbitrary (but consistent) endpoint of the origin edge.
085   */
086  public final N nodeU() {
087    return nodeU;
088  }
089
090  /**
091   * Returns the node {@link #adjacentNode(Object) adjacent} to {@link #nodeU()} along the origin
092   * edge. If this {@link EndpointPair} {@link #isOrdered()}, this is equal to {@link #target()}.
093   */
094  public final N nodeV() {
095    return nodeV;
096  }
097
098  /**
099   * Returns the node that is adjacent to {@code node} along the origin edge.
100   *
101   * @throws IllegalArgumentException if this {@link EndpointPair} does not contain {@code node}
102   */
103  public final N adjacentNode(Object node) {
104    if (node.equals(nodeU)) {
105      return nodeV;
106    } else if (node.equals(nodeV)) {
107      return nodeU;
108    } else {
109      throw new IllegalArgumentException(
110          String.format("EndpointPair %s does not contain node %s", this, node));
111    }
112  }
113
114  /**
115   * Returns {@code true} iff this {@link EndpointPair} is an ordered pair (i.e. represents the
116   * endpoints of a directed edge).
117   */
118  public abstract boolean isOrdered();
119
120  /** Iterates in the order {@link #nodeU()}, {@link #nodeV()}. */
121  @Override
122  public final UnmodifiableIterator<N> iterator() {
123    return Iterators.forArray(nodeU, nodeV);
124  }
125
126  /**
127   * Two ordered {@link EndpointPair}s are equal if their {@link #source()} and {@link #target()}
128   * are equal. Two unordered {@link EndpointPair}s are equal if they contain the same nodes. An
129   * ordered {@link EndpointPair} is never equal to an unordered {@link EndpointPair}.
130   */
131  @Override
132  public abstract boolean equals(@Nullable Object obj);
133
134  /**
135   * The hashcode of an ordered {@link EndpointPair} is equal to {@code Objects.hashCode(source(),
136   * target())}. The hashcode of an unordered {@link EndpointPair} is equal to {@code
137   * nodeU().hashCode() ^ nodeV().hashCode()}.
138   */
139  @Override
140  public abstract int hashCode();
141
142  private static final class Ordered<N> extends EndpointPair<N> {
143    private Ordered(N source, N target) {
144      super(source, target);
145    }
146
147    @Override
148    public N source() {
149      return nodeU();
150    }
151
152    @Override
153    public N target() {
154      return nodeV();
155    }
156
157    @Override
158    public boolean isOrdered() {
159      return true;
160    }
161
162    @Override
163    public boolean equals(@Nullable Object obj) {
164      if (obj == this) {
165        return true;
166      }
167      if (!(obj instanceof EndpointPair)) {
168        return false;
169      }
170
171      EndpointPair<?> other = (EndpointPair<?>) obj;
172      if (isOrdered() != other.isOrdered()) {
173        return false;
174      }
175
176      return source().equals(other.source()) && target().equals(other.target());
177    }
178
179    @Override
180    public int hashCode() {
181      return Objects.hashCode(source(), target());
182    }
183
184    @Override
185    public String toString() {
186      return String.format("<%s -> %s>", source(), target());
187    }
188  }
189
190  private static final class Unordered<N> extends EndpointPair<N> {
191    private Unordered(N nodeU, N nodeV) {
192      super(nodeU, nodeV);
193    }
194
195    @Override
196    public N source() {
197      throw new UnsupportedOperationException(NOT_AVAILABLE_ON_UNDIRECTED);
198    }
199
200    @Override
201    public N target() {
202      throw new UnsupportedOperationException(NOT_AVAILABLE_ON_UNDIRECTED);
203    }
204
205    @Override
206    public boolean isOrdered() {
207      return false;
208    }
209
210    @Override
211    public boolean equals(@Nullable Object obj) {
212      if (obj == this) {
213        return true;
214      }
215      if (!(obj instanceof EndpointPair)) {
216        return false;
217      }
218
219      EndpointPair<?> other = (EndpointPair<?>) obj;
220      if (isOrdered() != other.isOrdered()) {
221        return false;
222      }
223
224      // Equivalent to the following simple implementation:
225      // boolean condition1 = nodeU().equals(other.nodeU()) && nodeV().equals(other.nodeV());
226      // boolean condition2 = nodeU().equals(other.nodeV()) && nodeV().equals(other.nodeU());
227      // return condition1 || condition2;
228      if (nodeU().equals(other.nodeU())) { // check condition1
229        // Here's the tricky bit. We don't have to explicitly check for condition2 in this case.
230        // Why? The second half of condition2 requires that nodeV equals other.nodeU.
231        // We already know that nodeU equals other.nodeU. Combined with the earlier statement,
232        // and the transitive property of equality, this implies that nodeU equals nodeV.
233        // If nodeU equals nodeV, condition1 == condition2, so checking condition1 is sufficient.
234        return nodeV().equals(other.nodeV());
235      }
236      return nodeU().equals(other.nodeV()) && nodeV().equals(other.nodeU()); // check condition2
237    }
238
239    @Override
240    public int hashCode() {
241      return nodeU().hashCode() ^ nodeV().hashCode();
242    }
243
244    @Override
245    public String toString() {
246      return String.format("[%s, %s]", nodeU(), nodeV());
247    }
248  }
249}