/*
* Copyright 2014 McDowell
*
* 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 uk.kludje.experimental.fluent;
import uk.kludje.fn.nary.TetraConsumer;
import uk.kludje.fn.nary.TriConsumer;
import uk.kludje.fn.nary.TriFunction;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* Type for mutating an object using method references.
*
* @param <T> the type of the underlying value
*/
public final class Mutator<T> {
private final T t;
private Mutator(T t) {
this.t = t;
}
/**
* Creates a new mutate instance.
*
* @param t the non-null type to adapt
* @param <T> the adapted type
* @return a new instance
*/
public static <T> Mutator<T> mutate(T t) {
Objects.requireNonNull(t);
return new Mutator<>(t);
}
/**
* Passes the underlying value to a consumer.
* Usage:
* <pre>
* AtomicInteger two = mutate(new AtomicInteger())
* .nil(AtomicInteger::incrementAndGet)
* .nil(AtomicInteger::incrementAndGet)
* .get();
* </pre>
*
* @param consumer typically a method reference for T
* @return this
*/
public Mutator<T> nil(Consumer<? super T> consumer) {
consumer.accept(t);
return this;
}
/**
* Passes the underlying value to a consumer, with an argument.
* Usage:
* <pre>
* List<String> list = mutate(new ArrayList<String>())
* .nil(List::add, "a")
* .nil(List::add, "b")
* .nil(List::add, "c")
* .nil(List::remove, "b")
* .map(Collections::unmodifiableList)
* .get();
* </pre>
*
* @param consumer typically a method reference for T
* @param a an argument
* @param <A> argument type
* @return this
*/
public <A> Mutator<T> un(BiConsumer<? super T, A> consumer, A a) {
consumer.accept(t, a);
return this;
}
/**
* Passes the underlying value to a consumer, with two arguments.
* Usage:
* <pre>
* Map<String, String> map = mutate(new HashMap<String, String>())
* .nil(Map::put, "a", "A")
* .nil(Map::put, "b", "B")
* .nil(Map::put, "c", "C")
* .map(Collections::unmodifiableMap)
* .get();
* </pre>
*
* @param consumer typically a method reference for T
* @param a first argument
* @param b second argument
* @param <A> type of a
* @param <B> type of b
* @return this
*/
public <A, B> Mutator<T> bi(TriConsumer<? super T, A, B> consumer, A a, B b) {
consumer.accept(t, a, b);
return this;
}
public <A, B, C> Mutator<T> tri(TetraConsumer<? super T, A, B, C> consumer, A a, B b, C c) {
consumer.accept(t, a, b, c);
return this;
}
public Nullary<T> nullary(Consumer<? super T> consumer) {
Objects.requireNonNull(consumer);
return new Nullary<>(this, consumer);
}
public <A> Unary<T, A> unary(BiConsumer<? super T, A> consumer) {
Objects.requireNonNull(consumer);
return new Unary<>(this, consumer);
}
public <A, B> Binary<T, A, B> binary(TriConsumer<? super T, A, B> consumer) {
Objects.requireNonNull(consumer);
return new Binary<>(this, consumer);
}
public <A, B, C> Trinary<T, A, B, C> binary(TetraConsumer<? super T, A, B, C> consumer) {
Objects.requireNonNull(consumer);
return new Trinary<>(this, consumer);
}
/**
* Unwraps the value.
*
* @return the underlying value
*/
public T get() {
return t;
}
public <M> Mutator<M> map(Function<T, M> mapper) {
return new Mutator<M>(mapper.apply(t));
}
public <M, A> Mutator<M> map(BiFunction<T, A, M> mapper, A a) {
return new Mutator<M>(mapper.apply(t, a));
}
public <M, A, B> Mutator<M> map(TriFunction<T, A, B, M> mapper, A a, B b) {
return new Mutator<M>(mapper.apply(t, a, b));
}
/**
* A string of undefined form for debugging purposes.
*
* @return a string form.
*/
@Override
public String toString() {
return "Fluent{" + t + "}";
}
public static abstract class FluentMethod<T> {
private final Mutator<T> parent;
private FluentMethod(Mutator<T> parent) {
this.parent = parent;
}
public Mutator<T> unbind() {
return parent;
}
public T get() {
return parent.t;
}
}
public static class Nullary<T> extends FluentMethod<T> {
private final Consumer<? super T> consumer;
private Nullary(Mutator<T> parent, Consumer<? super T> consumer) {
super(parent);
this.consumer = consumer;
}
public Nullary<T> invoke() {
consumer.accept(get());
return this;
}
}
public static class Unary<T, A> extends FluentMethod<T> {
private final BiConsumer<? super T, A> consumer;
private Unary(Mutator<T> parent, BiConsumer<? super T, A> consumer) {
super(parent);
this.consumer = consumer;
}
public Unary<T, A> invoke(A a) {
consumer.accept(get(), a);
return this;
}
}
public static class Binary<T, A, B> extends FluentMethod<T> {
private final TriConsumer<? super T, A, B> consumer;
private Binary(Mutator<T> parent, TriConsumer<? super T, A, B> consumer) {
super(parent);
this.consumer = consumer;
}
public Binary<T, A, B> invoke(A a, B b) {
consumer.accept(get(), a, b);
return this;
}
}
public static class Trinary<T, A, B, C> extends FluentMethod<T> {
private final TetraConsumer<? super T, A, B, C> consumer;
private Trinary(Mutator<T> parent, TetraConsumer<? super T, A, B, C> consumer) {
super(parent);
this.consumer = consumer;
}
public Trinary<T, A, B, C> invoke(A a, B b, C c) {
consumer.accept(get(), a, b, c);
return this;
}
}
}