package com.github.czyzby.kiwi.util.tuple.immutable;
import java.util.Iterator;
import com.github.czyzby.kiwi.util.common.Nullables;
import com.github.czyzby.kiwi.util.tuple.TripleTuple;
import com.github.czyzby.kiwi.util.tuple.Tuples;
import com.github.czyzby.kiwi.util.tuple.mutable.MutableTriple;
/** Triple tuple. Stores three variables. Immutable utility container for all these cases that three variables have to
* be returned by a function or otherwise passed and stored together. Implements Map.Entry using first variable as key
* and second as value for additional utility, although it obviously doesn't store just 2 values. Additionally, it
* doesn't allow changing its value with setValue (throws AssertionError).
*
* @author MJ */
public class Triple<First, Second, Third> implements TripleTuple<First, Second, Third> {
private static final long serialVersionUID = 1L;
private final First first;
private final Second second;
private final Third third;
public Triple(final First first, final Second second, final Third third) {
this.first = first;
this.second = second;
this.third = third;
}
/** @return a new triplet with the passed variables. Accepts nulls. */
public static <First, Second, Third> Triple<First, Second, Third> of(final First first, final Second second,
final Third third) {
return new Triple<First, Second, Third>(first, second, third);
}
/** @return a new triplet with the passed variables.
* @throws NullPointerException if any of the variables are null. */
public static <First, Second, Third> Triple<First, Second, Third> ofNonNull(final First first, final Second second,
final Third third) throws NullPointerException {
if (first == null || second == null || third == null) {
throw new NullPointerException("Tried to construct non-nullable triplet with null value.");
}
return new Triple<First, Second, Third>(first, second, third);
}
/** @param triplet will be inverted.
* @return a new triplet with inverted first and third values. */
public static <First, Second, Third> Triple<Third, Second, First> invert(
final Triple<First, Second, Third> triplet) {
return new Triple<Third, Second, First>(triplet.getThird(), triplet.getSecond(), triplet.getFirst());
}
/** @param triplet will be shifted left.
* @return a new triplet with values shifted left. */
public static <First, Second, Third> Triple<Second, Third, First> shiftLeft(
final Triple<First, Second, Third> triplet) {
return new Triple<Second, Third, First>(triplet.getSecond(), triplet.getThird(), triplet.getFirst());
}
/** @param triplet will be shifted right.
* @return a new triplet with values shifted right. */
public static <First, Second, Third> Triple<Third, First, Second> shiftRight(
final Triple<First, Second, Third> triplet) {
return new Triple<Third, First, Second>(triplet.getThird(), triplet.getFirst(), triplet.getSecond());
}
@Override
public First getFirst() {
return first;
}
@Override
public boolean isFirstPresent() {
return first != null;
}
@Override
public Second getSecond() {
return second;
}
@Override
public boolean isSecondPresent() {
return second != null;
}
@Override
public Third getThird() {
return third;
}
@Override
public boolean isThirdPresent() {
return second != null;
}
@Override
public First getKey() {
return first;
}
@Override
public Second getValue() {
return second;
}
@Override
@Deprecated
/** @throws UnsupportedOperationException on each call. Triple is immutable. */
public Second setValue(final Second value) {
throw new UnsupportedOperationException("Triple cannot be modified.");
}
@Override
public Object get(final int index) throws IndexOutOfBoundsException {
// Generally a little bit faster than a switch.
if (index == 0) {
return first;
}
if (index == 1) {
return second;
}
if (index == 2) {
return third;
}
throw new IndexOutOfBoundsException("Invalid index passed to triplet: " + index + ".");
}
@Override
@SuppressWarnings("unchecked")
public <Type> Type get(final int index, final Class<Type> asInstanceOf) throws IndexOutOfBoundsException {
// Generally a little bit faster than switch.
if (index == 0) {
return (Type) first;
}
if (index == 1) {
return (Type) second;
}
if (index == 2) {
return (Type) third;
}
throw new IndexOutOfBoundsException("Invalid index passed to triplet: " + index + ".");
}
@Override
public Triple<Third, Second, First> invert() {
return of(third, second, first);
}
@Override
public Triple<Second, Third, First> shiftLeft() {
return of(second, third, first);
}
@Override
public Triple<Third, First, Second> shitfRight() {
return of(third, first, second);
}
/** @return a new instance of MutableTriplet constructed with this triplet's values. */
public MutableTriple<First, Second, Third> toMutable() {
return MutableTriple.of(first, second, third);
}
@Override
public int getSize() {
return SIZE;
}
@Override
public int hashCode() {
int hash = 0;
if (first != null) {
hash = first.hashCode();
}
if (second != null) {
hash ^= second.hashCode();
}
if (third != null) {
hash ^= third.hashCode();
}
return hash;
}
@Override
public boolean equals(final Object object) {
return object == this || object instanceof TripleTuple
&& Nullables.areEqual(first, ((TripleTuple<?, ?, ?>) object).getFirst())
&& Nullables.areEqual(second, ((TripleTuple<?, ?, ?>) object).getSecond())
&& Nullables.areEqual(third, ((TripleTuple<?, ?, ?>) object).getThird());
}
@Override
public String toString() {
return first + COMMA_WITH_SPACE_SEPARATOR + second + COMMA_WITH_SPACE_SEPARATOR + third;
}
@Override
public Iterator<Object> iterator() {
return Tuples.getTupleIterator(this);
}
@Override
public boolean contains(final Object value) {
return Nullables.areEqual(value, first) || Nullables.areEqual(value, second)
|| Nullables.areEqual(value, third);
}
@Override
public boolean containsAll(final Object... values) {
for (final Object value : values) {
if (!contains(value)) {
return false;
}
}
return true;
}
@Override
public boolean containsAll(final Iterable<?> values) {
for (final Object value : values) {
if (!contains(value)) {
return false;
}
}
return true;
}
@Override
public boolean containsAny(final Object... values) {
for (final Object value : values) {
if (contains(value)) {
return true;
}
}
return false;
}
@Override
public boolean containsAny(final Iterable<?> values) {
for (final Object value : values) {
if (contains(value)) {
return true;
}
}
return false;
}
@Override
public int indexOf(final Object value) {
return Nullables.areEqual(value, first) ? 0
: Nullables.areEqual(value, second) ? 1 : Nullables.areEqual(value, third) ? 2 : INVALID_INDEX;
}
@Override
public boolean isMutable() {
return false;
}
@Override
@Deprecated
/** @throws UnsupportedOperationException on each call. Triple is immutable. */
public void set(final int index, final Object value) {
throw new UnsupportedOperationException("Triple cannot be modified.");
}
@Override
public Object[] toArray() {
return new Object[] { first, second, third };
}
@Override
@SuppressWarnings("unchecked")
public <Type> Type[] toArray(final Type[] array) {
array[0] = (Type) first;
array[1] = (Type) second;
array[2] = (Type) third;
return array;
}
@Override
public <Type> Iterator<Type> iterator(final Class<Type> forClass) {
return Tuples.getTupleIterator(this);
}
}