/* * 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 com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.io.Serializable; import java.util.Iterator; import javax.annotation.Nullable; /** * 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