package org.bitbucket.cowwoc.guava.stream;
import com.google.common.collect.Table;
import java.util.Comparator;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import org.bitbucket.cowwoc.preconditions.Preconditions;
/**
* Builds a stream {@link Collector} that returns a {@link Table}.
*
* @author Gili Tzabari
* @param the type of the input elements
* @param the type of rows stored in the table
* @param the type of columns stored in the table
* @param the type of values stored in the table
* @param the output type of the collector
*/
public abstract class TableCollectorBuilder>
{
private final Function super T, ? extends R> rowMapper;
private final Function super T, ? extends C> columnMapper;
private final Function super T, ? extends V> valueMapper;
private Supplier> supplier;
private Function, O> finisher;
private Characteristics[] characteristics;
private Comparator super R> rowComparator;
private Comparator super C> columnComparator;
/**
* Creates a new builder.
*
* @param rowMapper a function that transforms the input into table rows
* @param columnMapper a function that transforms the input into table columns
* @param valueMapper a function that transforms the input into table values
* @param supplier a function which returns a new, empty {@code Table} into which
* intermediate results will be added
* @param finisher a function that transforms the intermediate {@code Table} into the final
* result
* @param characteristics the collector characteristics
* @throws NullPointerException if any of the arguments are null
*/
public TableCollectorBuilder(Function super T, ? extends R> rowMapper,
Function super T, ? extends C> columnMapper, Function super T, ? extends V> valueMapper,
Supplier
> supplier, Function, O> finisher,
Characteristics... characteristics)
{
Preconditions.requireThat(rowMapper, "rowMapper").isNotNull();
Preconditions.requireThat(columnMapper, "columnMapper").isNotNull();
Preconditions.requireThat(valueMapper, "valueMapper").isNotNull();
Preconditions.requireThat(supplier, "supplier").isNotNull();
Preconditions.requireThat(finisher, "finisher").isNotNull();
Preconditions.requireThat(characteristics, "characteristics").isNotNull();
this.rowMapper = rowMapper;
this.columnMapper = columnMapper;
this.valueMapper = valueMapper;
this.supplier = supplier;
this.finisher = finisher;
this.characteristics = characteristics;
}
/**
* @return a function that transforms the input into table rows
*/
public Function super T, ? extends R> rowMapper()
{
return rowMapper;
}
/**
* @return a function that transforms the input into table columns
*/
public Function super T, ? extends C> columnMapper()
{
return columnMapper;
}
/**
* @return a function that transforms the input into table values
*/
public Function super T, ? extends V> valueMapper()
{
return valueMapper;
}
/**
* @return a function which returns a new, empty {@code Table} into which intermediate results
* will be added
*/
public Supplier> supplier()
{
return supplier;
}
/**
* @param supplier a function which returns a new, empty {@code Table} into which intermediate
* results will be added
* @return this
* @throws NullPointerException if supplier is null
*/
public TableCollectorBuilder supplier(Supplier> supplier)
throws NullPointerException
{
Preconditions.requireThat(supplier, "supplier").isNotNull();
this.supplier = supplier;
return this;
}
/**
* @return a function that transforms the intermediate {@code Table} into the final result
*/
public Function, O> finisher()
{
return finisher;
}
/**
* Sets a function that transforms the intermediate {@code Table} into the final result.
*
* Make sure to update the collector
* {@link #characteristics(java.util.stream.Collector.Characteristics[]) characteristics} if
* necessary.
*
* @param finisher a function that transforms the intermediate {@code Table} into the final result
* @return this
* @throws NullPointerException if finisher is null
*/
public TableCollectorBuilder finisher(Function, O> finisher)
throws NullPointerException
{
Preconditions.requireThat(finisher, "finisher").isNotNull();
this.finisher = finisher;
return this;
}
/**
* @return the collector characteristics
*/
@SuppressWarnings("ReturnOfCollectionOrArrayField")
public Characteristics[] characteristics()
{
return characteristics;
}
/**
* @param characteristics the collector characteristics
* @return this
* @throws NullPointerException if characteristics is null
*/
@SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
public TableCollectorBuilder characteristics(
Characteristics... characteristics) throws NullPointerException
{
Preconditions.requireThat(characteristics, "characteristics").isNotNull();
this.characteristics = characteristics;
return this;
}
/**
* @return a new collector
*/
public Collector build()
{
BiConsumer, T> accumulator = (table, input) ->
{
R row = rowMapper.apply(input);
if (row == null)
throw new IllegalArgumentException("rowMapper(" + input + ") returned null");
C column = columnMapper.apply(input);
if (column == null)
throw new IllegalArgumentException("rowMapper(" + input + ") returned null");
V value = valueMapper.apply(input);
if (value == null)
throw new IllegalArgumentException("valueMapper(" + input + ") returned null");
table.put(row, column, value);
};
BinaryOperator> combiner = (left, right) ->
{
left.putAll(right);
return left;
};
return Collector.of(supplier, accumulator, combiner, finisher, characteristics);
}
}