package eu.ehri.project.indexing;
import com.google.common.collect.Lists;
import eu.ehri.project.indexing.converter.Converter;
import eu.ehri.project.indexing.converter.impl.MultiConverter;
import eu.ehri.project.indexing.sink.Sink;
import eu.ehri.project.indexing.sink.impl.MultiSink;
import eu.ehri.project.indexing.sink.impl.NoopSink;
import eu.ehri.project.indexing.source.Source;
import eu.ehri.project.indexing.source.impl.MultiSource;
import eu.ehri.project.indexing.source.impl.NoopSource;
import java.util.List;
/**
* A class to orchestrate flow from sources, converters,
* and sinks.
*/
public class Pipeline<S, E> {
protected final Source<? extends S> source;
protected final Sink<? super E> writer;
protected final Converter< S, ? extends E> converter;
/**
* Pipeline builder.
*/
public static class Builder<S, E> {
private final List<Source<? extends S>> sources = Lists.newArrayList();
private final List<Converter< S, ? extends E>> converters = Lists.newArrayList();
private final List<Sink<? super E>> writers = Lists.newArrayList();
public Builder<S, E> addSink(Sink<E> writer) {
writers.add(writer);
return this;
}
public Builder<S, E> addSource(Source<S> source) {
this.sources.add(source);
return this;
}
public Builder<S, E> addConverter(Converter<S, ? extends E> converter) {
this.converters.add(converter);
return this;
}
private Sink<? super E> getSink() {
if (writers.size() > 1) {
return new MultiSink<>(writers);
} else if (writers.size() == 1) {
return writers.get(0);
} else {
return new NoopSink<>();
}
}
private Source<? extends S> getSource() {
if (sources.size() > 1) {
return new MultiSource<>(sources);
} else if (sources.size() == 1) {
return sources.get(0);
} else {
return new NoopSource<>();
}
}
private Converter<S, ? extends E> getConverter() {
if (converters.size() > 1) {
return new MultiConverter<>(converters);
} else if (converters.size() == 1) {
return converters.get(0);
} else {
throw new IllegalStateException("No converters defined " +
"for mapping sources to sinks");
}
}
/**
* Build a pipeline with the given sources, converters,
* and sinks.
*
* @return a new pipeline
* @throws IllegalStateException if there are no converters provided
*/
public Pipeline<S, E> build() {
return new Pipeline<>(getSource(), getConverter(), getSink());
}
}
protected Pipeline(Source<? extends S> source,
Converter<S, ? extends E> converter,
Sink<? super E> sink) {
this.writer = sink;
this.source = source;
this.converter = converter;
}
/**
* Initiate the pipeline.
*
* @throws eu.ehri.project.indexing.source.Source.SourceException when a source errors
* @throws eu.ehri.project.indexing.sink.Sink.SinkException when a sink errors
* @throws eu.ehri.project.indexing.converter.Converter.ConverterException when a converter errors
*/
public void run() throws Source.SourceException, Sink.SinkException, Converter.ConverterException {
try {
for (S item : source.iterable()) {
for (E out : converter.convert(item)) {
writer.write(out);
}
}
} finally {
source.close();
writer.close();
}
}
}