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}