/*
* 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.GwtCompatible;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.ForOverride;
import com.google.errorprone.annotations.concurrent.LazyInit;
import java.io.Serializable;
import java.util.Iterator;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
A function from A
to B
with an associated reverse function from B
to A
; used for converting back and forth between different representations of the same
information.
Invertibility
The reverse operation may be a strict inverse (meaning that
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 Doubles.stringConverter
:
stringConverter().convert("1.00")
returns the Double
value 1.0
stringConverter().reverse().convert(1.0)
returns the string "1.0"
-- not the same string ("1.00"
) we started with
Note that it should still be the case that the round-tripped and original objects are
similar.
Nullability
A converter always converts null
to null
and non-null references to non-null references. It would not make sense to consider 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 convert
method handles this null behavior for all converters; implementations of doForward
and doBackward
are guaranteed to never be passed null
, and must never return null
.
Common ways to use
Getting a converter:
- Use a provided converter implementation, such as
Enums.stringConverter
, Ints.stringConverter
or the reverse views of these. - Convert between specific preset values using
Maps.asConverter
. For example, use this to create a "fake" converter for a unit test. It is unnecessary (and confusing) to mock the Converter
type using a mocking framework. - Extend this class and implement its
doForward
and doBackward
methods. - Java 8 users: you may prefer to pass two lambda expressions or method references to the
from
factory method.
Using a converter:
- Convert one instance in the "forward" direction using
converter.convert(a)
. - Convert multiple instances "forward" using
converter.convertAll(as)
. - Convert in the "backward" direction using
converter.reverse().convert(b)
or
converter.reverse().convertAll(bs)
. - Use
converter
or converter.reverse()
anywhere a Function
is accepted (for example
Stream.map
). - Do not call
doForward
or doBackward
directly; these exist only to be overridden.
Example
return new Converter<Integer, String>() {
protected String doForward(Integer i) {
return Integer.toHexString(i);
}
protected Integer doBackward(String s) {
return parseUnsignedInt(s, 16);
}
};
An alternative using Java 8:
return Converter.from(
Integer::toHexString,
s -> parseUnsignedInt(s, 16));
Author: Mike Ward, Kurt Alfred Kluever, Gregory Kick Since: 16.0
/**
* A function from {@code A} to {@code B} with an associated <i>reverse</i> function from {@code B}
* to {@code A}; used for converting back and forth between <i>different representations of the same
* information</i>.
*
* <h3>Invertibility</h3>
*
* <p>The reverse operation <b>may</b> be a strict <i>inverse</i> (meaning that {@code
* converter.reverse().convert(converter.convert(a)).equals(a)} is always true). However, it is very
* common (perhaps <i>more</i> common) for round-trip conversion to be <i>lossy</i>. Consider an
* example round-trip using {@link com.google.common.primitives.Doubles#stringConverter}:
*
* <ol>
* <li>{@code stringConverter().convert("1.00")} returns the {@code Double} value {@code 1.0}
* <li>{@code stringConverter().reverse().convert(1.0)} returns the string {@code "1.0"} --
* <i>not</i> the same string ({@code "1.00"}) we started with
* </ol>
*
* <p>Note that it should still be the case that the round-tripped and original objects are
* <i>similar</i>.
*
* <h3>Nullability</h3>
*
* <p>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
* <i>missing</i> 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}.
*
*
* <h3>Common ways to use</h3>
*
* <p>Getting a converter:
*
* <ul>
* <li>Use a provided converter implementation, such as {@link Enums#stringConverter}, {@link
* com.google.common.primitives.Ints#stringConverter Ints.stringConverter} or the {@linkplain
* #reverse reverse} views of these.
* <li>Convert between specific preset values using {@link
* com.google.common.collect.Maps#asConverter Maps.asConverter}. For example, use this to
* create a "fake" converter for a unit test. It is unnecessary (and confusing) to <i>mock</i>
* the {@code Converter} type using a mocking framework.
* <li>Extend this class and implement its {@link #doForward} and {@link #doBackward} methods.
* <li><b>Java 8 users:</b> you may prefer to pass two lambda expressions or method references to
* the {@link #from from} factory method.
* </ul>
*
* <p>Using a converter:
*
* <ul>
* <li>Convert one instance in the "forward" direction using {@code converter.convert(a)}.
* <li>Convert multiple instances "forward" using {@code converter.convertAll(as)}.
* <li>Convert in the "backward" direction using {@code converter.reverse().convert(b)} or {@code
* converter.reverse().convertAll(bs)}.
* <li>Use {@code converter} or {@code converter.reverse()} anywhere a {@link
* java.util.function.Function} is accepted (for example {@link java.util.stream.Stream#map
* Stream.map}).
* <li><b>Do not</b> call {@link #doForward} or {@link #doBackward} directly; these exist only to
* be overridden.
* </ul>
*
* <h3>Example</h3>
*
* <pre>
* return new Converter<Integer, String>() {
* protected String doForward(Integer i) {
* return Integer.toHexString(i);
* }
*
* protected Integer doBackward(String s) {
* return parseUnsignedInt(s, 16);
* }
* };</pre>
*
* <p>An alternative using Java 8:
*
* <pre>{@code
* return Converter.from(
* Integer::toHexString,
* s -> parseUnsignedInt(s, 16));
* }</pre>
*
* @author Mike Ward
* @author Kurt Alfred Kluever
* @author Gregory Kick
* @since 16.0
*/
@GwtCompatible
public abstract class Converter<A, B> implements Function<A, B> {
private final boolean handleNullAutomatically;
// We lazily cache the reverse view to avoid allocating on every call to reverse().
@LazyInit private transient @MonotonicNonNull Converter<B, A> reverse;
Constructor for use by subclasses. /** Constructor for use by subclasses. */
protected Converter() {
this(true);
}
Constructor used only by LegacyConverter
to suspend automatic null-handling. /** Constructor used only by {@code LegacyConverter} to suspend automatic null-handling. */
Converter(boolean handleNullAutomatically) {
this.handleNullAutomatically = handleNullAutomatically;
}
// SPI methods (what subclasses must implement)
Returns a representation of a
as an instance of type B
. If a
cannot be converted, an unchecked exception (such as IllegalArgumentException
) should be thrown. Params: - a – the instance to convert; will never be null
Returns: the converted instance; must not be null
/**
* Returns a representation of {@code a} as an instance of type {@code B}. If {@code a} cannot be
* converted, an unchecked exception (such as {@link IllegalArgumentException}) should be thrown.
*
* @param a the instance to convert; will never be null
* @return the converted instance; <b>must not</b> be null
*/
@ForOverride
protected abstract B doForward(A a);
Returns a representation of b
as an instance of type A
. If b
cannot be converted, an unchecked exception (such as IllegalArgumentException
) should be thrown. Params: - b – the instance to convert; will never be null
Throws: - UnsupportedOperationException – if backward conversion is not implemented; this should be
very rare. Note that if backward conversion is not only unimplemented but
unimplementable (for example, consider a
Converter<Chicken, ChickenNugget>
), then this is not logically a Converter
at all, and should just implement Function
.
Returns: the converted instance; must not be null
/**
* Returns a representation of {@code b} as an instance of type {@code A}. If {@code b} cannot be
* converted, an unchecked exception (such as {@link IllegalArgumentException}) should be thrown.
*
* @param b the instance to convert; will never be null
* @return the converted instance; <b>must not</b> be null
* @throws UnsupportedOperationException if backward conversion is not implemented; this should be
* very rare. Note that if backward conversion is not only unimplemented but
* unimplement<i>able</i> (for example, consider a {@code Converter<Chicken, ChickenNugget>}),
* then this is not logically a {@code Converter} at all, and should just implement {@link
* Function}.
*/
@ForOverride
protected abstract A doBackward(B b);
// API (consumer-side) methods
Returns a representation of a
as an instance of type B
. Returns: the converted value; is null if and only if a
is null
/**
* Returns a representation of {@code a} as an instance of type {@code B}.
*
* @return the converted value; is null <i>if and only if</i> {@code a} is null
*/
@CanIgnoreReturnValue
public final @Nullable B convert(@Nullable A a) {
return correctedDoForward(a);
}
@Nullable
B correctedDoForward(@Nullable A a) {
if (handleNullAutomatically) {
// TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert?
return a == null ? null : checkNotNull(doForward(a));
} else {
return doForward(a);
}
}
@Nullable
A correctedDoBackward(@Nullable B b) {
if (handleNullAutomatically) {
// TODO(kevinb): we shouldn't be checking for a null result at runtime. Assert?
return b == null ? null : checkNotNull(doBackward(b));
} else {
return doBackward(b);
}
}
Returns an iterable that applies convert
to each element of fromIterable
. The conversion is done lazily. The returned iterable's iterator supports remove()
if the input iterator does. After a successful remove()
call, fromIterable
no longer contains the corresponding element.
/**
* Returns an iterable that applies {@code convert} to each element of {@code fromIterable}. The
* conversion is done lazily.
*
* <p>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.
*/
@CanIgnoreReturnValue
public Iterable<B> convertAll(final Iterable<? extends A> fromIterable) {
checkNotNull(fromIterable, "fromIterable");
return new Iterable<B>() {
@Override
public Iterator<B> iterator() {
return new Iterator<B>() {
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 this.convert(a)
back to a value roughly equivalent to a
. The returned converter is serializable if this
converter is.
Note: you should not override this method. It is non-final for legacy reasons.
/**
* Returns the reversed view of this converter, which converts {@code this.convert(a)} back to a
* value roughly equivalent to {@code a}.
*
* <p>The returned converter is serializable if {@code this} converter is.
*
* <p><b>Note:</b> you should not override this method. It is non-final for legacy reasons.
*/
@CanIgnoreReturnValue
public Converter<B, A> reverse() {
Converter<B, A> result = reverse;
return (result == null) ? reverse = new ReverseConverter<>(this) : result;
}
private static final class ReverseConverter<A, B> extends Converter<B, A>
implements Serializable {
final Converter<A, B> original;
ReverseConverter(Converter<A, B> 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<A, B> 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 convert
method applies secondConverter
to the result of this converter. Its reverse
method applies the converters in reverse order. The returned converter is serializable if this
converter and secondConverter
are.
/**
* 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.
*
* <p>The returned converter is serializable if {@code this} converter and {@code secondConverter}
* are.
*/
public final <C> Converter<A, C> andThen(Converter<B, C> secondConverter) {
return doAndThen(secondConverter);
}
Package-private non-final implementation of andThen() so only we can override it. /** Package-private non-final implementation of andThen() so only we can override it. */
<C> Converter<A, C> doAndThen(Converter<B, C> secondConverter) {
return new ConverterComposition<>(this, checkNotNull(secondConverter));
}
private static final class ConverterComposition<A, B, C> extends Converter<A, C>
implements Serializable {
final Converter<A, B> first;
final Converter<B, C> second;
ConverterComposition(Converter<A, B> first, Converter<B, C> second) {
this.first = first;
this.second = second;
}
/*
* These gymnastics are a little confusing. Basically this class has neither legacy nor
* non-legacy behavior; it just needs to let the behaviors of the backing converters shine
* through (which might even differ from each other!). So, we override the correctedDo* methods,
* after which the do* methods should never be reached.
*/
@Override
protected C doForward(A a) {
throw new AssertionError();
}
@Override
protected A doBackward(C c) {
throw new AssertionError();
}
@Override
@Nullable
C correctedDoForward(@Nullable A a) {
return second.correctedDoForward(first.correctedDoForward(a));
}
@Override
@Nullable
A correctedDoBackward(@Nullable C c) {
return first.correctedDoBackward(second.correctedDoBackward(c));
}
@Override
public boolean equals(@Nullable Object object) {
if (object instanceof ConverterComposition) {
ConverterComposition<?, ?, ?> that = (ConverterComposition<?, ?, ?>) object;
return this.first.equals(that.first) && this.second.equals(that.second);
}
return false;
}
@Override
public int hashCode() {
return 31 * first.hashCode() + second.hashCode();
}
@Override
public String toString() {
return first + ".andThen(" + second + ")";
}
private static final long serialVersionUID = 0L;
}
Deprecated: Provided to satisfy the Function
interface; use convert
instead.
/**
* @deprecated Provided to satisfy the {@code Function} interface; use {@link #convert} instead.
*/
@Deprecated
@Override
@CanIgnoreReturnValue
public final @Nullable B apply(@Nullable A a) {
return convert(a);
}
Indicates whether another object is equal to this converter.
Most implementations will have no reason to override the behavior of Object.equals
. However, an implementation may also choose to return true
whenever object
is a Converter
that it considers interchangeable with this one. "Interchangeable"
typically means that Objects.equal(this.convert(a), that.convert(a))
is true for all a
of type A
(and similarly for reverse
). Note that a false
result from this method does not imply that the converters are known not to be
interchangeable.
/**
* Indicates whether another object is equal to this converter.
*
* <p>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 <i>interchangeable</i> with this one. "Interchangeable"
* <i>typically</i> 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 <i>not</i> to be
* interchangeable.
*/
@Override
public boolean equals(@Nullable Object object) {
return super.equals(object);
}
// Static converters
Returns a converter based on separate forward and backward functions. This is useful if the function instances already exist, or so that you can supply lambda expressions. If those circumstances don't apply, you probably don't need to use this; subclass Converter
and implement its doForward
and doBackward
methods directly. These functions will never be passed null
and must not under any circumstances return null
. If a value cannot be converted, the function should throw an unchecked exception (typically, but not necessarily, IllegalArgumentException
).
The returned converter is serializable if both provided functions are.
Since: 17.0
/**
* Returns a converter based on separate forward and backward functions. This is useful if the
* function instances already exist, or so that you can supply lambda expressions. If those
* circumstances don't apply, you probably don't need to use this; subclass {@code Converter} and
* implement its {@link #doForward} and {@link #doBackward} methods directly.
*
* <p>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}).
*
* <p>The returned converter is serializable if both provided functions are.
*
* @since 17.0
*/
public static <A, B> Converter<A, B> from(
Function<? super A, ? extends B> forwardFunction,
Function<? super B, ? extends A> backwardFunction) {
return new FunctionBasedConverter<>(forwardFunction, backwardFunction);
}
private static final class FunctionBasedConverter<A, B> extends Converter<A, B>
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. /** Returns a serializable converter that always converts or reverses an object to itself. */
@SuppressWarnings("unchecked") // implementation is "fully variant"
public static <T> Converter<T, T> identity() {
return (IdentityConverter<T>) IdentityConverter.INSTANCE;
}
A converter that always converts or reverses an object to itself. Note that T is now a
"pass-through type".
/**
* A converter that always converts or reverses an object to itself. Note that T is now a
* "pass-through type".
*/
private static final class IdentityConverter<T> extends Converter<T, T> implements Serializable {
static final IdentityConverter INSTANCE = new IdentityConverter();
@Override
protected T doForward(T t) {
return t;
}
@Override
protected T doBackward(T t) {
return t;
}
@Override
public IdentityConverter<T> reverse() {
return this;
}
@Override
<S> Converter<T, S> doAndThen(Converter<T, S> otherConverter) {
return checkNotNull(otherConverter, "otherConverter");
}
/*
* We *could* override convertAll() to return its input, but it's a rather pointless
* optimization and opened up a weird type-safety problem.
*/
@Override
public String toString() {
return "Converter.identity()";
}
private Object readResolve() {
return INSTANCE;
}
private static final long serialVersionUID = 0L;
}
}