/* * Copyright (C) 2008 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.base; import static com.google.common.base.Preconditions.checkNotNull; import java.io.Serializable; import java.util.Iterator; import javax.annotation.Nullable; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; /** * A function from {@code A} to {@code B} with an associated reverse * function from {@code B} to {@code A}; used for converting back and forth * between different representations of the same information. * *
* The reverse operation may be a strict inverse (meaning that * {@code * converter.reverse().convert(converter.convert(a)).equals(a)} is always true). * However, it is very common (perhaps more common) for round-trip * conversion to be lossy. Consider an example round-trip using * {@link com.google.common.primitives.Doubles#stringConverter}: * *
* Note that it should still be the case that the round-tripped and original * objects are similar. * *
* A converter always converts {@code null} to {@code null} and non-null * references to non-null references. It would not make sense to consider * {@code null} and a non-null reference to be "different representations of the * same information", since one is distinguishable from missing * information and the other is not. The {@link #convert} method handles this * null behavior for all converters; implementations of {@link #doForward} and * {@link #doBackward} are guaranteed to never be passed {@code null}, and must * never return {@code null}. * * *
* Getting a converter: * *
* Using a converter: * *
* The returned iterable's iterator supports {@code remove()} if the input * iterator does. After a successful {@code remove()} call, {@code fromIterable} * no longer contains the corresponding element. */ public Iterable convertAll(final Iterable extends A> fromIterable) { checkNotNull(fromIterable, "fromIterable"); return new Iterable() { @Override public Iterator iterator() { return new Iterator() { private final Iterator extends A> fromIterator = fromIterable.iterator(); @Override public boolean hasNext() { return fromIterator.hasNext(); } @Override public B next() { return convert(fromIterator.next()); } @Override public void remove() { fromIterator.remove(); } }; } }; } /** * Returns the reversed view of this converter, which converts * {@code this.convert(a)} back to a value roughly equivalent to {@code a}. * *
* The returned converter is serializable if {@code this} converter is.
*/
// TODO(user): Make this method final
public Converter reverse() {
Converter result = reverse;
return (result == null) ? reverse = new ReverseConverter(this) : result;
}
private static final class ReverseConverter extends Converter implements Serializable {
final Converter original;
ReverseConverter(Converter original) {
this.original = original;
}
/*
* These gymnastics are a little confusing. Basically this class has neither
* legacy nor non-legacy behavior; it just needs to let the behavior of the
* backing converter shine through. So, we override the correctedDo* methods,
* after which the do* methods should never be reached.
*/
@Override
protected A doForward(B b) {
throw new AssertionError();
}
@Override
protected B doBackward(A a) {
throw new AssertionError();
}
@Override
@Nullable
A correctedDoForward(@Nullable B b) {
return original.correctedDoBackward(b);
}
@Override
@Nullable
B correctedDoBackward(@Nullable A a) {
return original.correctedDoForward(a);
}
@Override
public Converter reverse() {
return original;
}
@Override
public boolean equals(@Nullable Object object) {
if (object instanceof ReverseConverter) {
ReverseConverter, ?> that = (ReverseConverter, ?>) object;
return this.original.equals(that.original);
}
return false;
}
@Override
public int hashCode() {
return ~original.hashCode();
}
@Override
public String toString() {
return original + ".reverse()";
}
private static final long serialVersionUID = 0L;
}
/**
* Returns a converter whose {@code convert} method applies
* {@code secondConverter} to the result of this converter. Its {@code reverse}
* method applies the converters in reverse order.
*
*
* The returned converter is serializable if {@code this} converter and
* {@code secondConverter} are.
*/
public
* Most implementations will have no reason to override the behavior of
* {@link Object#equals}. However, an implementation may also choose to return
* {@code true} whenever {@code object} is a {@link Converter} that it considers
* interchangeable with this one. "Interchangeable" typically
* means that {@code Objects.equal(this.convert(a), that.convert(a))} is true
* for all {@code a} of type {@code A} (and similarly for {@code reverse}). Note
* that a {@code false} result from this method does not imply that the
* converters are known not to be interchangeable.
*/
@Override
public boolean equals(@Nullable Object object) {
return super.equals(object);
}
// Static converters
/**
* Returns a converter based on existing forward and backward functions.
* Note that it is unnecessary to create new classes implementing
* {@code Function} just to pass them in here. Instead, simply subclass
* {@code Converter} and implement its {@link #doForward} and
* {@link #doBackward} methods directly.
*
*
* These functions will never be passed {@code null} and must not under any
* circumstances return {@code null}. If a value cannot be converted, the
* function should throw an unchecked exception (typically, but not necessarily,
* {@link IllegalArgumentException}).
*
*
* The returned converter is serializable if both provided functions are.
*
* @since 17.0
*/
public static Converter from(Function super A, ? extends B> forwardFunction,
Function super B, ? extends A> backwardFunction) {
return new FunctionBasedConverter(forwardFunction, backwardFunction);
}
private static final class FunctionBasedConverter extends Converter implements Serializable {
private final Function super A, ? extends B> forwardFunction;
private final Function super B, ? extends A> backwardFunction;
private FunctionBasedConverter(Function super A, ? extends B> forwardFunction,
Function super B, ? extends A> backwardFunction) {
this.forwardFunction = checkNotNull(forwardFunction);
this.backwardFunction = checkNotNull(backwardFunction);
}
@Override
protected B doForward(A a) {
return forwardFunction.apply(a);
}
@Override
protected A doBackward(B b) {
return backwardFunction.apply(b);
}
@Override
public boolean equals(@Nullable Object object) {
if (object instanceof FunctionBasedConverter) {
FunctionBasedConverter, ?> that = (FunctionBasedConverter, ?>) object;
return this.forwardFunction.equals(that.forwardFunction)
&& this.backwardFunction.equals(that.backwardFunction);
}
return false;
}
@Override
public int hashCode() {
return forwardFunction.hashCode() * 31 + backwardFunction.hashCode();
}
@Override
public String toString() {
return "Converter.from(" + forwardFunction + ", " + backwardFunction + ")";
}
}
/**
* Returns a serializable converter that always converts or reverses an object
* to itself.
*/
@SuppressWarnings("unchecked") // implementation is "fully variant"
public static Converter