/* * Copyright 2016 DiffPlug * * 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.diffplug.common.base; import java.util.function.Function; import javax.annotation.Nullable; /** * A {@link Converter} which may receive null and may return null. */ public interface ConverterNullable<A, B> { /** * Creates a converter using the given functions, * with the given name shown in "toString()". */ public static <A, B> ConverterNullable<A, B> from( Function<? super A, ? extends B> forwardFunction, Function<? super B, ? extends A> backwardFunction, String name) { return new ConverterNullable<A, B>() { @Override public B convert(A a) { return forwardFunction.apply(a); } @Override public A revert(B b) { return backwardFunction.apply(b); } @Override public String toString() { return name; } }; } public static <A, B> ConverterNullable<A, B> from( Function<? super A, ? extends B> forwardFunction, Function<? super B, ? extends A> backwardFunction) { return from(forwardFunction, backwardFunction, forwardFunction.toString()); } /** * 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; possibly null * @return the converted instance; possibly null */ @Nullable B convert(@Nullable A a); /** * 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; possibly null * @return the converted instance; possibly 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}. */ @Nullable A revert(@Nullable B b); /** * 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. */ default <C> ConverterNullable<A, C> andThen(ConverterNullable<B, C> andThen) { return from( a -> andThen.convert(convert(a)), c -> revert(andThen.revert(c)), toString() + " andThen " + andThen.toString()); } /** * Returns the reversed view of this converter, where the {@link #convert(Object)} * and {@link #revert(Object)} methods are swapped. */ default ConverterNullable<B, A> reverse() { return new ReverseConverter<B, A>(this); } static class ReverseConverter<A, B> implements ConverterNullable<A, B> { final ConverterNullable<B, A> original; ReverseConverter(ConverterNullable<B, A> original) { this.original = original; } @Override public B convert(A a) { return original.revert(a); } @Override public A revert(B b) { return original.convert(b); } @Override public ConverterNullable<B, A> reverse() { return original; } @Override public String toString() { return original.toString() + ".reverse()"; } } }