All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.common.graph.EndpointPair Maven / Gradle / Ivy

/*
 * Copyright (C) 2016 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.graph;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.graph.GraphConstants.NOT_AVAILABLE_ON_UNDIRECTED;

import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import com.google.common.collect.Iterators;
import com.google.common.collect.UnmodifiableIterator;
import com.google.errorprone.annotations.Immutable;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;

/**
 * An immutable pair representing the two endpoints of an edge in a graph. The {@link EndpointPair}
 * of a directed edge is an ordered pair of nodes ({@link #source()} and {@link #target()}). The
 * {@link EndpointPair} of an undirected edge is an unordered pair of nodes ({@link #nodeU()} and
 * {@link #nodeV()}).
 *
 * 

The edge is a self-loop if, and only if, the two endpoints are equal. * * @author James Sexton * @since 20.0 */ @Beta @Immutable(containerOf = {"N"}) public abstract class EndpointPair implements Iterable { private final N nodeU; private final N nodeV; private EndpointPair(N nodeU, N nodeV) { this.nodeU = checkNotNull(nodeU); this.nodeV = checkNotNull(nodeV); } /** Returns an {@link EndpointPair} representing the endpoints of a directed edge. */ public static EndpointPair ordered(N source, N target) { return new Ordered(source, target); } /** Returns an {@link EndpointPair} representing the endpoints of an undirected edge. */ public static EndpointPair unordered(N nodeU, N nodeV) { // Swap nodes on purpose to prevent callers from relying on the "ordering" of an unordered pair. return new Unordered(nodeV, nodeU); } /** Returns an {@link EndpointPair} representing the endpoints of an edge in {@code graph}. */ static EndpointPair of(Graph graph, N nodeU, N nodeV) { return graph.isDirected() ? ordered(nodeU, nodeV) : unordered(nodeU, nodeV); } /** Returns an {@link EndpointPair} representing the endpoints of an edge in {@code network}. */ static EndpointPair of(Network network, N nodeU, N nodeV) { return network.isDirected() ? ordered(nodeU, nodeV) : unordered(nodeU, nodeV); } /** * If this {@link EndpointPair} {@link #isOrdered()}, returns the node which is the source. * * @throws UnsupportedOperationException if this {@link EndpointPair} is not ordered */ public abstract N source(); /** * If this {@link EndpointPair} {@link #isOrdered()}, returns the node which is the target. * * @throws UnsupportedOperationException if this {@link EndpointPair} is not ordered */ public abstract N target(); /** * If this {@link EndpointPair} {@link #isOrdered()} returns the {@link #source()}; otherwise, * returns an arbitrary (but consistent) endpoint of the origin edge. */ public final N nodeU() { return nodeU; } /** * Returns the node {@link #adjacentNode(Object) adjacent} to {@link #nodeU()} along the origin * edge. If this {@link EndpointPair} {@link #isOrdered()}, this is equal to {@link #target()}. */ public final N nodeV() { return nodeV; } /** * Returns the node that is adjacent to {@code node} along the origin edge. * * @throws IllegalArgumentException if this {@link EndpointPair} does not contain {@code node} */ public final N adjacentNode(Object node) { if (node.equals(nodeU)) { return nodeV; } else if (node.equals(nodeV)) { return nodeU; } else { throw new IllegalArgumentException("EndpointPair " + this + " does not contain node " + node); } } /** * Returns {@code true} if this {@link EndpointPair} is an ordered pair (i.e. represents the * endpoints of a directed edge). */ public abstract boolean isOrdered(); /** Iterates in the order {@link #nodeU()}, {@link #nodeV()}. */ @Override public final UnmodifiableIterator iterator() { return Iterators.forArray(nodeU, nodeV); } /** * Two ordered {@link EndpointPair}s are equal if their {@link #source()} and {@link #target()} * are equal. Two unordered {@link EndpointPair}s are equal if they contain the same nodes. An * ordered {@link EndpointPair} is never equal to an unordered {@link EndpointPair}. */ @Override public abstract boolean equals(@NullableDecl Object obj); /** * The hashcode of an ordered {@link EndpointPair} is equal to {@code Objects.hashCode(source(), * target())}. The hashcode of an unordered {@link EndpointPair} is equal to {@code * nodeU().hashCode() + nodeV().hashCode()}. */ @Override public abstract int hashCode(); private static final class Ordered extends EndpointPair { private Ordered(N source, N target) { super(source, target); } @Override public N source() { return nodeU(); } @Override public N target() { return nodeV(); } @Override public boolean isOrdered() { return true; } @Override public boolean equals(@NullableDecl Object obj) { if (obj == this) { return true; } if (!(obj instanceof EndpointPair)) { return false; } EndpointPair other = (EndpointPair) obj; if (isOrdered() != other.isOrdered()) { return false; } return source().equals(other.source()) && target().equals(other.target()); } @Override public int hashCode() { return Objects.hashCode(source(), target()); } @Override public String toString() { return "<" + source() + " -> " + target() + ">"; } } private static final class Unordered extends EndpointPair { private Unordered(N nodeU, N nodeV) { super(nodeU, nodeV); } @Override public N source() { throw new UnsupportedOperationException(NOT_AVAILABLE_ON_UNDIRECTED); } @Override public N target() { throw new UnsupportedOperationException(NOT_AVAILABLE_ON_UNDIRECTED); } @Override public boolean isOrdered() { return false; } @Override public boolean equals(@NullableDecl Object obj) { if (obj == this) { return true; } if (!(obj instanceof EndpointPair)) { return false; } EndpointPair other = (EndpointPair) obj; if (isOrdered() != other.isOrdered()) { return false; } // Equivalent to the following simple implementation: // boolean condition1 = nodeU().equals(other.nodeU()) && nodeV().equals(other.nodeV()); // boolean condition2 = nodeU().equals(other.nodeV()) && nodeV().equals(other.nodeU()); // return condition1 || condition2; if (nodeU().equals(other.nodeU())) { // check condition1 // Here's the tricky bit. We don't have to explicitly check for condition2 in this case. // Why? The second half of condition2 requires that nodeV equals other.nodeU. // We already know that nodeU equals other.nodeU. Combined with the earlier statement, // and the transitive property of equality, this implies that nodeU equals nodeV. // If nodeU equals nodeV, condition1 == condition2, so checking condition1 is sufficient. return nodeV().equals(other.nodeV()); } return nodeU().equals(other.nodeV()) && nodeV().equals(other.nodeU()); // check condition2 } @Override public int hashCode() { return nodeU().hashCode() + nodeV().hashCode(); } @Override public String toString() { return "[" + nodeU() + ", " + nodeV() + "]"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy