ListBuilder.java
package com.brianmearns.crafter;
import com.brianmearns.crafter.util.SupplierFunctions;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
/**
* A {@link BuilderInterface} for a list of values.
*
* @author Brian Mearns <bmearns@ieee.org>
*/
@SuppressWarnings("unused")
public abstract class ListBuilder<T> implements BuilderInterface<List<T>> {
/**
* Static factory method to create an instance.
*/
@Nonnull
public static <T> ListBuilder<T> create() {
return new DefaultListBuilder<>();
}
/**
* Static factory method to create an instance.
* @param cls Specifies the type {@code T} of the builder returned.
*/
@Nonnull
public static <T> ListBuilder<T> create(Class<T> cls) {
return new DefaultListBuilder<>();
}
/**
* Helper method for adding an element as a supplier of that element.
* @param element Supplier of the element to add.
*
* @return {@code this} object itself, for chaining convenience.
*/
@Nonnull
protected abstract ListBuilder<T> add(@Nonnull Supplier<? extends T> element);
/**
* Helper method for adding an iterable of suppliers of elements.
*
* @param elements Suppliers for all the elements to add.
*
* @return {@code this} object itself, for chaining convenience.
*/
@Nonnull
protected abstract ListBuilder<T> addSuppliers(@Nonnull Iterable<? extends Supplier<? extends T>> elements);
/**
* Helper method for adding an iterator of suppliers of elements.
*
* @param elements Suppliers for all the elements to add.
*
* @return {@code this} object itself, for chaining convenience.
*/
@Nonnull
protected abstract ListBuilder<T> addSuppliers(@Nonnull Iterator<? extends Supplier<? extends T>> elements);
/**
* Helper method for adding an array of suppliers of elements.
*
* @param elements Suppliers for all the elements to add.
*
* @return {@code this} object itself, for chaining convenience.
*/
@Nonnull
protected abstract ListBuilder<T> addSuppliers(@Nonnull Supplier<? extends T>[] elements);
/**
* Helper method to add the given element, if and only if the given boolean is {@code true}. Otherwise
* has no effect on the state of the builder.
*
* @param element A supplier for the element to {@link #add(Supplier)}.
* @param add Whether or not to add the element.
*
* @return {@code this} object itself, for chaining convenience.
*/
@Nonnull
protected abstract ListBuilder<T> maybeAdd(@Nonnull Supplier<? extends T> element, boolean add);
/**
* Add all of the given elements, in order, to the list of values.
*
* @param elements An {@link Iterable} of the elements to add.
*
* @return {@code this} object itself, for chaining convenience.
*
* @see #addAll(Iterator)
* @see #addAll(Object[])
* @see #addBuilders(Iterable)
*/
@Nonnull
public ListBuilder<T> addAll(@Nonnull Iterable<? extends T> elements) {
return addSuppliers(Iterables.transform(elements, SupplierFunctions.<T>supplierOfInstanceFunction()));
}
/**
* Add all of the given {@link BuilderInterface Builders}, in order, as builders of element values to the end of the list.
*
* <p>
* As with {@link #add(BuilderInterface)}, this does not invoke any of the builders immediately to build the element value.
* Each builder will be invoked once during a call to {@link #get()}.
*
* @param elements An {@link Iterable} of the builders to add.
*
* @return {@code this} object itself, for chaining convenience.
*
* @see #addBuilders(Iterator)
* @see #addBuilders(BuilderInterface[])
* @see #addAll(Iterable)
*/
@Nonnull
public ListBuilder<T> addBuilders(@Nonnull Iterable<? extends BuilderInterface<? extends T>> elements) {
return addSuppliers(elements);
}
/**
* Add all of the given elements, in order, to the list of values.
*
* @param elements An {@link Iterator} over the elements to add.
*
* @return {@code this} object itself, for chaining convenience.
*
* @see #addAll(Iterable)
* @see #addAll(Object[])
* @see #addBuilders(Iterator)
*/
@Nonnull
public ListBuilder<T> addAll(@Nonnull Iterator<? extends T> elements) {
return addSuppliers(Iterators.transform(elements, SupplierFunctions.<T>supplierOfInstanceFunction()));
}
/**
* Add all of the given {@link BuilderInterface Builders}, in order, as builders of element values to the end of the list.
*
* <p>
* As with {@link #add(BuilderInterface)}, this does not invoke any of the builders immediately to build the element value.
* Each builder will be invoked once during a call to {@link #get()}.
*
* @param elements An {@link Iterator} over the builders to add.
*
* @return {@code this} object itself, for chaining convenience.
*
* @see #addBuilders(Iterable)
* @see #addBuilders(BuilderInterface[])
* @see #addAll(Iterator)
*/
@Nonnull
public ListBuilder<T> addBuilders(@Nonnull Iterator<? extends BuilderInterface<? extends T>> elements) {
return addSuppliers(elements);
}
/**
* Add all of the given elements, in order, to the list of values.
*
* @param elements An array of the elements to add.
*
* @return {@code this} object itself, for chaining convenience.
*
* @see #addAll(Iterable)
* @see #addAll(Iterator)
* @see #addBuilders(BuilderInterface[])
*/
@Nonnull
public ListBuilder<T> addAll(@Nonnull T[] elements) {
return addSuppliers(Iterables.transform(Arrays.asList(elements), SupplierFunctions.<T>supplierOfInstanceFunction()));
}
/**
* Add all of the given {@link BuilderInterface Builders}, in order, as builders of element values to the end of the list.
*
* <p>
* As with {@link #add(BuilderInterface)}, this does not invoke any of the builders immediately to build the element value.
* Each builder will be invoked once during a call to {@link #get()}.
*
* @param elements An array of the builders to add.
*
* @return {@code this} object itself, for chaining convenience.
*
* @see #addBuilders(Iterable)
* @see #addBuilders(Iterator)
* @see #addAll(Object[])
*/
@Nonnull
public ListBuilder<T> addBuilders(@Nonnull BuilderInterface<? extends T>[] elements) {
return addSuppliers(elements);
}
/**
* Add the given item as the next element in the list.
*
* @param element The element to append to the list.
*
* @return {@code this} object itself, for chaining convenience.
*/
@Nonnull
public ListBuilder<T> add(@Nullable T element) {
return add(Suppliers.ofInstance(element));
}
/**
* Add the given {@link BuilderInterface} as a builder for the next item in the list. The builder is not invoked to build
* the value immediately, its {@link BuilderInterface#get()} method is invoked to build the element value only when <em>this</em>
* object's {@link #get()} method is invoked to build a list of values.
*
* @param elementBuilder Builder for the element to put at the end of the current list of elements.
*
* @return {@code this} object itself, for chaining convenience.
*/
@Nonnull
public ListBuilder<T> add(@Nonnull BuilderInterface<? extends T> elementBuilder) {
return add((Supplier<? extends T>) elementBuilder);
}
/**
* Adds the given element, if and only if the given boolean is {@code true}. Otherwise
* has no effect on the state of the builder.
*
* @param element The element value to {@link #add(Object)}.
* @param add Whether or not to add the element.
*
* @return {@code this} object itself, for chaining convenience.
*/
@Nonnull
public ListBuilder<T> maybeAdd(@Nullable T element, boolean add) {
return maybeAdd(Suppliers.ofInstance(element), add);
}
/**
* Build a new list using the elements specified for this builder.
*
* @return The built list of elements.
*/
@Nonnull
@Override
public abstract List<T> get() throws IncompleteBuilderException;
/**
* Adds the given element builder for the next item in the list, if and only if the
* given boolean is {@code true}. Otherwise has no effect on the state of the builder.
*
* @param element The element value to {@link #add(BuilderInterface)}.
* @param add Whether or not to add the element.
*
* @return {@code this} object itself, for chaining convenience.
*/
@Nonnull
public ListBuilder<T> maybeAdd(@Nonnull BuilderInterface<? extends T> element, boolean add) {
return maybeAdd((Supplier<? extends T>) element, add);
}
/**
* Apply the given function to {@code this} object, and return {@code this} object again.
*
* <p>
* This is simply a way to add some arbitrary code in the middle of a chain of method invocations.
* You could use the {@link Function} to perform some complex logic to configure the builder, for instance.
*
* <p>
* The return value of the function is ignored, but for safety and clarity, it should be a void return.
*
* @param function The {@link Function} to be invoked on {@code this} object.
*
* @return This {@code ListBuilder} itself, for chaining convenience.
*/
@Nonnull
public abstract ListBuilder<T> apply(@Nonnull Function<ListBuilder<T>, Void> function);
/**
* Returns the top-level non-conditional builder.
*/
@Nonnull
public abstract ListBuilder<T> always();
/**
* Returns a builder which either does or doesn't delegate to this builder based on the given boolean.
* @param yes If {@code true}, then methods invoked on the returned builder will modify the state of {@code this}
* builder. Otherwise, methods invoked on the returned builder will not modify state.
*/
@Nonnull
public abstract ListBuilder<T> maybe(boolean yes);
/**
* Returns the parent list builder of a conditional list builder. This is not necessarily the originating top level
* builder if you have nested (or rather chained) calls to {@link #maybe(boolean)}.
*/
@Nonnull
public abstract ListBuilder<T> endMaybe();
@SuppressWarnings("unused")
protected static class DefaultListBuilder<T> extends ListBuilder<T> {
@Nonnull
private final List<Supplier<? extends T>> elements;
{
//Linked list is good because we're mostly just appending to it, and then iterating through it.
// Linked lists are pretty good at both.
elements = new LinkedList<>();
}
public DefaultListBuilder() {
}
/**
* Helper method to actually build the list returned by {@link #get()}.
*
* <p>
* This can be overridden if you need to create a different type of {@link List}. The
* default implementation produces an {@link ArrayList}.
*
* <p>
* Be careful how you implement this. The given {@link Supplier suppliers} should
* be {@linkplain Supplier#get() invoked} exactly once per call to this method, and
* they should be invoked from within this method. For instance, things like
* {@link Lists#transform(List, Function)} do "lazy" evaluation which invokes the
* supplier everytime the element is accessed in the returned list. This is <em>not</em>
* suitable for use with this method.
*
* @param suppliers An immutable list of the suppliers for the element values.
*
* @return A list that will be returned by this object's {@link #get()} method.
*/
@Nonnull
protected List<T> get(@Nonnull ImmutableList<Supplier<? extends T>> suppliers) {
ArrayList<T> list = new ArrayList<>(suppliers.size());
for (Supplier<? extends T> supplier : suppliers) {
list.add(supplier.get());
}
return list;
}
@Override
@Nonnull
protected ListBuilder<T> add(@Nonnull Supplier<? extends T> element) {
elements.add(element);
return this;
}
@Override
@Nonnull
protected ListBuilder<T> addSuppliers(@Nonnull Iterable<? extends Supplier<? extends T>> elements) {
Iterables.addAll(this.elements, elements);
return this;
}
@Override
@Nonnull
protected ListBuilder<T> addSuppliers(@Nonnull Iterator<? extends Supplier<? extends T>> elements) {
while(elements.hasNext()) {
this.elements.add(elements.next());
}
return this;
}
@Override
@Nonnull
protected ListBuilder<T> addSuppliers(@Nonnull Supplier<? extends T>[] elements) {
return addSuppliers(Arrays.asList(elements));
}
@Override
@Nonnull
protected ListBuilder<T> maybeAdd(@Nonnull Supplier<? extends T> element, boolean add) {
if(add) {
this.add(element);
}
return this;
}
@Override
@Nonnull
public ListBuilder<T> apply(@Nonnull Function<ListBuilder<T>, Void> function) {
function.apply(this);
return this;
}
/**
* Build a new list using the elements specified for this builder.
*
* <p>
* Note that this delegates to {@link #get(ImmutableList)}.
* </p>
*
* @return The built list of elements.
*/
@Nonnull
@Override
public List<T> get() throws IncompleteBuilderException {
return get(ImmutableList.copyOf(elements));
}
/**
* Returns itself.
*/
@Override
@Nonnull
public ListBuilder<T> always() {
return this;
}
/**
* Returns either {@code this} object itself, or a new {@link NeverListBuilder} if {@code yes} is {@code false}.
*/
@Override
@Nonnull
public ListBuilder<T> maybe(boolean yes) {
if(yes) {
return this;
} else {
return new NeverListBuilder<>(this, this);
}
}
@Nonnull
@Override
public ListBuilder<T> endMaybe() {
return this;
}
}
/**
* A {@link ListBuilder} which doesn't actually do anything.
*/
protected static class NeverListBuilder<T> extends ListBuilder<T> {
@Nonnull
private final ListBuilder<T> alwaysBuilder;
@Nonnull
private final ListBuilder<T> parent;
protected NeverListBuilder(@Nonnull ListBuilder<T> alwaysBuilder, @Nonnull ListBuilder<T> parent) {
this.alwaysBuilder = alwaysBuilder;
this.parent = parent;
}
@Override
@Nonnull
protected ListBuilder<T> add(@Nonnull Supplier<? extends T> element) {
return this;
}
@Override
@Nonnull
protected ListBuilder<T> addSuppliers(@Nonnull Iterable<? extends Supplier<? extends T>> elements) {
return this;
}
@Override
@Nonnull
protected ListBuilder<T> addSuppliers(@Nonnull Iterator<? extends Supplier<? extends T>> elements) {
return this;
}
@Override
@Nonnull
protected ListBuilder<T> addSuppliers(@Nonnull Supplier<? extends T>[] elements) {
return this;
}
@Override
@Nonnull
protected ListBuilder<T> maybeAdd(@Nonnull Supplier<? extends T> element, boolean add) {
return this;
}
/**
* Note that the never build <em>does not</em> invoke the given function at all, it simply returns itself, as usual.
*/
@Nonnull
@Override
public ListBuilder<T> apply(@Nonnull Function<ListBuilder<T>, Void> function) {
return this;
}
/**
* Returns the originating (non-conditional) list builder.
*/
@Override
@Nonnull
public ListBuilder<T> always() {
return alwaysBuilder;
}
/**
* Returns a new {@link NeverListBuilder} itself. Chaining two "maybe" calls is like an AND: so if this AND that,
* then change the state of the builder. But if the first one is already false, it doesn't matter what the second
* one is, the aggregate is false.
*/
@Override
@Nonnull
public ListBuilder<T> maybe(boolean yes) {
return new NeverListBuilder<>(alwaysBuilder, this);
}
@Nonnull
public ListBuilder<T> endMaybe() {
return parent;
}
/**
* Delegates to the originating (non-conditional) list builder.
*/
@Override
@Nonnull
public List<T> get() throws IncompleteBuilderException {
return alwaysBuilder.get();
}
}
}