/* * 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 static java.util.Objects.requireNonNull; import java.io.Serializable; import java.util.Iterator; import java.util.function.Function; /** * A {@link Converter} which may receive null and may return null. */ public interface Converter<A, B> { /** * Creates a converter using the given functions, * with the given name shown in "toString()". */ public static <A, B> Converter<A, B> from( Function<? super A, ? extends B> forwardFunction, Function<? super B, ? extends A> backwardFunction, String name) { return new FunctionBasedConverter<>(forwardFunction, backwardFunction, name); } static final class FunctionBasedConverter<A, B> implements Converter<A, B>, Serializable { private static final long serialVersionUID = 1L; final Function<? super A, ? extends B> forwardFunction; final Function<? super B, ? extends A> backwardFunction; final String name; private FunctionBasedConverter( Function<? super A, ? extends B> forwardFunction, Function<? super B, ? extends A> backwardFunction, String name) { this.forwardFunction = requireNonNull(forwardFunction); this.backwardFunction = requireNonNull(backwardFunction); this.name = requireNonNull(name); } @Override public B convertNonNull(A a) { return requireNonNull(forwardFunction.apply(a)); } @Override public A revertNonNull(B b) { return requireNonNull(backwardFunction.apply(b)); } @Override public boolean equals(Object object) { if (object instanceof FunctionBasedConverter) { FunctionBasedConverter<?, ?> that = (FunctionBasedConverter<?, ?>) object; return this.forwardFunction.equals(that.forwardFunction) && this.backwardFunction.equals(that.backwardFunction); } else { return false; } } @Override public int hashCode() { return forwardFunction.hashCode() * 31 + backwardFunction.hashCode(); } @Override public String toString() { return name; } } /** * 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; never null * @return the converted instance; never null */ B convertNonNull(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; never null * @return the converted instance; never 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}. */ A revertNonNull(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> Converter<A, C> andThen(Converter<B, C> andThen) { return new ConverterComposition<>(this, andThen); } static final class ConverterComposition<A, B, C> implements Converter<A, C>, Serializable { private static final long serialVersionUID = 1L; final Converter<A, B> first; final Converter<B, C> second; private ConverterComposition(Converter<A, B> first, Converter<B, C> second) { this.first = requireNonNull(first); this.second = requireNonNull(second); } @Override public C convertNonNull(A a) { return second.convertNonNull(first.convertNonNull(a)); } @Override public A revertNonNull(C c) { return first.revertNonNull(second.revertNonNull(c)); } @Override public boolean equals(Object object) { if (object instanceof ConverterComposition) { ConverterComposition<?, ?, ?> that = (ConverterComposition<?, ?, ?>) object; return this.first.equals(that.first) && this.second.equals(that.second); } else { return false; } } @Override public int hashCode() { return 31 * first.hashCode() + second.hashCode(); } @Override public String toString() { return first + ".andThen(" + second + ")"; } } /** * Returns the reversed view of this converter, where the {@link #convertNonNull(Object)} * and {@link #revertNonNull(Object)} methods are swapped. */ default Converter<B, A> reverse() { return new ReverseConverter<B, A>(this); } static class ReverseConverter<A, B> implements Converter<A, B>, Serializable { private static final long serialVersionUID = 1L; final Converter<B, A> original; ReverseConverter(Converter<B, A> original) { this.original = original; } @Override public B convertNonNull(A a) { return original.revertNonNull(a); } @Override public A revertNonNull(B b) { return original.convertNonNull(b); } @Override public Converter<B, A> reverse() { return original; } @Override public boolean equals(Object object) { if (object instanceof ReverseConverter) { ReverseConverter<?, ?> that = (ReverseConverter<?, ?>) object; return this.original.equals(that.original); } else { return false; } } @Override public int hashCode() { return ~original.hashCode(); } @Override public String toString() { return original.toString() + ".reverse()"; } } /** * 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. */ default Iterable<B> convertAll(Iterable<? extends A> fromIterable) { requireNonNull(fromIterable); return new Iterable<B>() { @Override public Iterator<B> iterator() { return new Iterator<B>() { final Iterator<? extends A> fromIterator = fromIterable.iterator(); @Override public boolean hasNext() { return fromIterator.hasNext(); } @Override public B next() { return convertNonNull(fromIterator.next()); } @Override public void remove() { fromIterator.remove(); } }; } }; } }