Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.gh.bmd.jrt.core.Channels Maven / Gradle / Ivy
/*
* 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.gh.bmd.jrt.core;
import com.gh.bmd.jrt.channel.InputChannel;
import com.gh.bmd.jrt.channel.OutputChannel;
import com.gh.bmd.jrt.channel.OutputConsumer;
import com.gh.bmd.jrt.channel.RoutineException;
import com.gh.bmd.jrt.channel.TemplateOutputConsumer;
import com.gh.bmd.jrt.channel.TransportChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Utility class for handling routine channels.
*
* Created by davide-maestroni on 3/15/15.
*/
public class Channels {
/**
* Avoid direct instantiation.
*/
protected Channels() {
}
/**
* Combines the specified channels into a selectable one. The selectable indexes will be the
* same as the list ones.
*
* @param channels the array of input channels.
* @return the selectable input channel.
* @throws java.lang.IllegalArgumentException if the specified array is empty.
*/
@Nonnull
public static InputChannel> combine(@Nonnull final InputChannel>... channels) {
return combine(0, channels);
}
/**
* Combines the specified channels into a selectable one.
*
* @param startIndex the selectable start index.
* @param channels the array of input channels.
* @return the selectable input channel.
* @throws java.lang.IllegalArgumentException if the specified array is empty.
*/
@Nonnull
public static InputChannel> combine(final int startIndex,
@Nonnull final InputChannel>... channels) {
final int length = channels.length;
if (length == 0) {
throw new IllegalArgumentException("the array of channels must not be empty");
}
final ArrayList> channelList = new ArrayList>(length);
Collections.addAll(channelList, channels);
final TransportChannel> transportChannel =
JRoutine.transport().buildChannel();
transportChannel.passTo(new SortingInputConsumer(startIndex, channelList));
return transportChannel;
}
/**
* Combines the specified channels into a selectable one.
*
* @param startIndex the selectable start index.
* @param channels the list of input channels.
* @return the selectable input channel.
* @throws java.lang.IllegalArgumentException if the specified list is empty.
*/
@Nonnull
public static InputChannel> combine(final int startIndex,
@Nonnull final List extends InputChannel>> channels) {
if (channels.isEmpty()) {
throw new IllegalArgumentException("the list of channels must not be empty");
}
final ArrayList> channelList = new ArrayList>(channels);
final TransportChannel> transportChannel =
JRoutine.transport().buildChannel();
transportChannel.passTo(new SortingInputConsumer(startIndex, channelList));
return transportChannel;
}
/**
* Combines the specified channels into a selectable one. The selectable indexes will be the
* same as the list ones.
*
* @param channels the list of input channels.
* @return the selectable input channel.
* @throws java.lang.IllegalArgumentException if the specified list is empty.
*/
@Nonnull
public static InputChannel> combine(
@Nonnull final List extends InputChannel>> channels) {
return combine(0, channels);
}
/**
* Combines the specified channels into a selectable one.
*
* @param channels the map of indexes and input channels.
* @return the selectable input channel.
* @throws java.lang.IllegalArgumentException if the specified map is empty.
*/
@Nonnull
public static InputChannel> combine(
@Nonnull final Map> channels) {
if (channels.isEmpty()) {
throw new IllegalArgumentException("the map of channels must not be empty");
}
final HashMap> channelMap =
new HashMap>(channels);
final TransportChannel> transportChannel =
JRoutine.transport().buildChannel();
transportChannel.passTo(new SortingInputMapConsumer(channelMap));
return transportChannel;
}
/**
* Returns a new channel distributing the input data among the specified channels. If the list
* of data exceeds the number of channels, the invocation will be aborted.
*
* @param channels the array of channels.
* @return the input channel.
* @throws java.lang.IllegalArgumentException if the specified array is empty.
*/
@Nonnull
public static InputChannel> distribute(@Nonnull final InputChannel>... channels) {
return distribute(false, channels);
}
/**
* Returns a new channel distributing the input data among the specified channels. If the list
* of data exceeds the number of channels, the invocation will be aborted.
*
* @param channels the list of channels.
* @return the input channel.
* @throws java.lang.IllegalArgumentException if the specified list is empty.
*/
@Nonnull
public static InputChannel> distribute(
@Nonnull final List extends InputChannel>> channels) {
return distribute(false, channels);
}
/**
* Returns a new channel distributing the input data among the specified channels. If the list
* of data is smaller of the specified number of channels, the remaining ones will be fed with
* null objects. While, if the list of data exceeds the number of channels, the invocation will
* be aborted.
*
* @param channels the array of channels.
* @return the input channel.
* @throws java.lang.IllegalArgumentException if the specified array is empty.
*/
@Nonnull
public static InputChannel> distributeAndFlush(
@Nonnull final InputChannel>... channels) {
return distribute(true, channels);
}
/**
* Returns a new channel distributing the input data among the specified channels. If the list
* of data is smaller of the specified number of channels, the remaining ones will be fed with
* null objects. While, if the list of data exceeds the number of channels, the invocation will
* be aborted.
*
* @param channels the list of channels.
* @return the input channel.
* @throws java.lang.IllegalArgumentException if the specified list is empty.
*/
@Nonnull
public static InputChannel> distributeAndFlush(
@Nonnull final List extends InputChannel>> channels) {
return distribute(true, channels);
}
/**
* Returns an output channel joining the data coming from the specified list of channels.
* An output will be generated only when at least one result is available for each channel.
*
* Note that the channels will be bound as a result of the call.
*
* @param channels the list of channels.
* @param the output data type.
* @return the output channel.
* @throws java.lang.IllegalArgumentException if the specified list is empty.
*/
@Nonnull
public static OutputChannel> join(
@Nonnull final List extends OutputChannel extends OUTPUT>> channels) {
return join(false, channels);
}
/**
* Returns an output channel joining the data coming from the specified list of channels.
* An output will be generated only when at least one result is available for each channel.
*
* Note that the channels will be bound as a result of the call.
*
* @param channels the array of channels.
* @return the output channel.
* @throws java.lang.IllegalArgumentException if the specified array is empty.
*/
@Nonnull
public static OutputChannel> join(@Nonnull final OutputChannel>... channels) {
return join(false, channels);
}
/**
* Returns an output channel joining the data coming from the specified list of channels.
* An output will be generated only when at least one result is available for each channel.
* Moreover, when all the output channels complete, the remaining output will be returned by
* filling the gaps with null instances, so that the generated list of data will always have the
* same size of the channel list.
*
* Note that the channels will be bound as a result of the call.
*
* @param channels the list of channels.
* @param the output data type.
* @return the output channel.
* @throws java.lang.IllegalArgumentException if the specified list is empty.
*/
@Nonnull
public static OutputChannel> joinAndFlush(
@Nonnull final List extends OutputChannel extends OUTPUT>> channels) {
return join(true, channels);
}
/**
* Returns an output channel joining the data coming from the specified list of channels.
* An output will be generated only when at least one result is available for each channel.
* Moreover, when all the output channels complete, the remaining output will be returned by
* filling the gaps with null instances, so that the generated list of data will always have the
* same size of the channel array.
*
* Note that the channels will be bound as a result of the call.
*
* @param channels the array of channels.
* @return the output channel.
* @throws java.lang.IllegalArgumentException if the specified array is empty.
*/
@Nonnull
public static OutputChannel> joinAndFlush(@Nonnull final OutputChannel>... channels) {
return join(true, channels);
}
/**
* Returns a map of input channels accepting the input data identified by the specified indexes.
*
* @param channel the selectable channel.
* @param indexes the collection of indexes.
* @param the channel data type.
* @param the input data type.
* @return the map of indexes and output channels.
*/
@Nonnull
public static Map> map(
@Nonnull final InputChannel super Selectable> channel,
@Nonnull final Collection indexes) {
final int size = indexes.size();
final HashMap> channelMap =
new HashMap>(size);
for (final Integer index : indexes) {
channelMap.put(index, Channels.select(channel, index));
}
return channelMap;
}
/**
* Returns a map of input channels accepting the input data identified by the specified indexes.
*
* @param channel the selectable channel.
* @param indexes the array of indexes.
* @param the channel data type.
* @param the input data type.
* @return the map of indexes and output channels.
*/
@Nonnull
public static Map> map(
@Nonnull final InputChannel super Selectable> channel,
@Nonnull final int... indexes) {
final int size = indexes.length;
final HashMap> channelMap =
new HashMap>(size);
for (final int index : indexes) {
channelMap.put(index, Channels.select(channel, index));
}
return channelMap;
}
/**
* Returns a map of input channels accepting the input data identified by the specified indexes.
*
* @param startIndex the selectable start index.
* @param rangeSize the size of the range of indexes (must be positive).
* @param channel the selectable channel.
* @param the channel data type.
* @param the input data type.
* @return the map of indexes and output channels.
* @throws java.lang.IllegalArgumentException if the specified range size is negative or 0.
*/
@Nonnull
public static Map> map(
final int startIndex, final int rangeSize,
@Nonnull final InputChannel super Selectable> channel) {
if (rangeSize <= 0) {
throw new IllegalArgumentException("invalid range size: " + rangeSize);
}
final HashMap> channelMap =
new HashMap>(rangeSize);
for (int index = startIndex; index < rangeSize; index++) {
channelMap.put(index, Channels.select(channel, index));
}
return channelMap;
}
/**
* Returns a map of output channels returning the output data filtered by the specified indexes.
*
* Note that the channel will be bound as a result of the call.
*
* @param startIndex the selectable start index.
* @param rangeSize the size of the range of indexes (must be positive).
* @param channel the selectable channel.
* @param the output data type.
* @return the map of indexes and output channels.
* @throws java.lang.IllegalArgumentException if the specified range size is negative or 0.
*/
@Nonnull
public static Map> map(final int startIndex,
final int rangeSize,
@Nonnull final OutputChannel extends Selectable extends OUTPUT>> channel) {
if (rangeSize <= 0) {
throw new IllegalArgumentException("invalid range size: " + rangeSize);
}
final HashMap> inputMap =
new HashMap>(rangeSize);
final HashMap> outputMap =
new HashMap>(rangeSize);
for (int index = startIndex; index < rangeSize; index++) {
final Integer integer = index;
final TransportChannel transportChannel = JRoutine.transport().buildChannel();
inputMap.put(integer, transportChannel);
outputMap.put(integer, transportChannel);
}
channel.passTo(new SortingOutputConsumer(inputMap));
return outputMap;
}
/**
* Returns a map of output channels returning the output data filtered by the specified indexes.
*
* Note that the channel will be bound as a result of the call.
*
* @param channel the selectable output channel.
* @param indexes the list of indexes.
* @param the output data type.
* @return the channel map.
*/
@Nonnull
public static Map> map(
@Nonnull final OutputChannel extends Selectable extends OUTPUT>> channel,
@Nonnull final Collection indexes) {
final int size = indexes.size();
final HashMap> inputMap =
new HashMap>(size);
final HashMap> outputMap =
new HashMap>(size);
for (final Integer index : indexes) {
final TransportChannel transportChannel = JRoutine.transport().buildChannel();
inputMap.put(index, transportChannel);
outputMap.put(index, transportChannel);
}
channel.passTo(new SortingOutputConsumer(inputMap));
return outputMap;
}
/**
* Returns a map of output channels returning the outputs filtered by the specified indexes.
*
* Note that the channel will be bound as a result of the call.
*
* @param channel the selectable output channel.
* @param indexes the list of indexes.
* @param the output data type.
* @return the channel map.
*/
@Nonnull
public static Map> map(
@Nonnull final OutputChannel extends Selectable extends OUTPUT>> channel,
@Nonnull final int... indexes) {
final int size = indexes.length;
final HashMap> inputMap =
new HashMap>(size);
final HashMap> outputMap =
new HashMap>(size);
for (final Integer index : indexes) {
final TransportChannel transportChannel = JRoutine.transport().buildChannel();
inputMap.put(index, transportChannel);
outputMap.put(index, transportChannel);
}
channel.passTo(new SortingOutputConsumer(inputMap));
return outputMap;
}
/**
* Merges the specified channels into a selectable one.
*
* Note that the channels will be bound as a result of the call.
*
* @param startIndex the selectable start index.
* @param channels the list of channels.
* @param the output data type.
* @return the selectable output channel.
* @throws java.lang.IllegalArgumentException if the specified list is empty.
*/
@Nonnull
public static OutputChannel extends Selectable> merge(final int startIndex,
@Nonnull final List extends OutputChannel extends OUTPUT>> channels) {
if (channels.isEmpty()) {
throw new IllegalArgumentException("the list of channels must not be empty");
}
final TransportChannel> transportChannel =
JRoutine.transport().buildChannel();
int i = startIndex;
for (final OutputChannel extends OUTPUT> channel : channels) {
transportChannel.pass(toSelectable(channel, i++));
}
return transportChannel.close();
}
/**
* Merges the specified channels into a selectable one.
*
* Note that the channels will be bound as a result of the call.
*
* @param startIndex the selectable start index.
* @param channels the array of channels.
* @return the selectable output channel.
* @throws java.lang.IllegalArgumentException if the specified array is empty.
*/
@Nonnull
public static OutputChannel extends Selectable>> merge(final int startIndex,
@Nonnull final OutputChannel>... channels) {
if (channels.length == 0) {
throw new IllegalArgumentException("the array of channels must not be empty");
}
final TransportChannel> transportChannel =
JRoutine.transport().buildChannel();
int i = startIndex;
for (final OutputChannel> channel : channels) {
transportChannel.pass(toSelectable(channel, i++));
}
return transportChannel.close();
}
/**
* Merges the specified channels into a selectable one.
*
* Note that the channels will be bound as a result of the call.
*
* @param channels the channels to merge.
* @param the output data type.
* @return the selectable output channel.
* @throws java.lang.IllegalArgumentException if the specified list is empty.
*/
@Nonnull
public static OutputChannel extends Selectable> merge(
@Nonnull final List extends OutputChannel extends OUTPUT>> channels) {
return merge(0, channels);
}
/**
* Merges the specified channels into a selectable one.
*
* Note that the channels will be bound as a result of the call.
*
* @param channelMap the map of indexes and output channels.
* @param the output data type.
* @return the selectable output channel.
* @throws java.lang.IllegalArgumentException if the specified map is empty.
*/
@Nonnull
public static OutputChannel extends Selectable> merge(
@Nonnull final Map> channelMap) {
if (channelMap.isEmpty()) {
throw new IllegalArgumentException("the map of channels must not be empty");
}
final TransportChannel> transportChannel =
JRoutine.transport().buildChannel();
for (final Entry> entry : channelMap
.entrySet()) {
transportChannel.pass(toSelectable(entry.getValue(), entry.getKey()));
}
return transportChannel.close();
}
/**
* Merges the specified channels into a selectable one.
*
* Note that the channels will be bound as a result of the call.
*
* @param channels the channels to merge.
* @return the selectable output channel.
* @throws java.lang.IllegalArgumentException if the specified array is empty.
*/
@Nonnull
public static OutputChannel extends Selectable>> merge(
@Nonnull final OutputChannel>... channels) {
return merge(0, channels);
}
/**
* Returns a new channel transforming the input data into selectable ones.
*
* @param channel the selectable channel.
* @param index the channel index.
* @param the channel data type.
* @param the input data type.
* @return the input channel.
*/
@Nonnull
public static InputChannel select(
@Nullable final InputChannel super Selectable> channel, final int index) {
final TransportChannel transportChannel = JRoutine.transport().buildChannel();
if (channel != null) {
transportChannel.passTo(new SelectableInputConsumer(channel, index));
}
return transportChannel;
}
/**
* Returns a new channel transforming the output data into selectable ones.
*
* Note that the channel will be bound as a result of the call.
*
* @param channel the selectable channel.
* @param index the channel index.
* @param the output data type.
* @return the output channel.
*/
@Nonnull
public static OutputChannel select(
@Nullable final OutputChannel extends Selectable extends OUTPUT>> channel,
final int index) {
final TransportChannel transportChannel = JRoutine.transport().buildChannel();
if (channel != null) {
channel.passTo(new FilterOutputConsumer(transportChannel, index));
}
return transportChannel;
}
/**
* Returns a new selectable channel feeding the specified one.
* Each output will be filtered based on the specified index.
*
* @param channel the channel to make selectable.
* @param index the channel index.
* @param the input data type.
* @return the selectable input channel.
*/
@Nonnull
public static InputChannel> toSelectable(
@Nullable final InputChannel super INPUT> channel, final int index) {
final TransportChannel> transportChannel =
JRoutine.transport().buildChannel();
if (channel != null) {
transportChannel.passTo(new FilterInputConsumer (channel, index));
}
return transportChannel;
}
/**
* Returns a new channel making the specified one selectable.
* Each output will be passed along unchanged.
*
* Note that the channel will be bound as a result of the call.
*
* @param channel the channel to make selectable.
* @param index the channel index.
* @param the output data type.
* @return the selectable output channel.
*/
@Nonnull
public static OutputChannel extends Selectable> toSelectable(
@Nullable final OutputChannel extends OUTPUT> channel, final int index) {
final TransportChannel> transportChannel =
JRoutine.transport().buildChannel();
if (channel != null) {
channel.passTo(new SelectableOutputConsumer(transportChannel, index));
}
return transportChannel;
}
@Nonnull
private static InputChannel> distribute(final boolean isFlush,
@Nonnull final InputChannel>... channels) {
final int length = channels.length;
if (length == 0) {
throw new IllegalArgumentException("the array of channels must not be empty");
}
final ArrayList> channelList = new ArrayList>(length);
Collections.addAll(channelList, channels);
final TransportChannel> transportChannel = JRoutine.transport().buildChannel();
return transportChannel.passTo(new DistributeInputConsumer(isFlush, channelList));
}
@Nonnull
private static InputChannel> distribute(final boolean isFlush,
@Nonnull final List extends InputChannel>> channels) {
if (channels.isEmpty()) {
throw new IllegalArgumentException("the list of channels must not be empty");
}
final ArrayList> channelList = new ArrayList>(channels);
final TransportChannel> transportChannel = JRoutine.transport().buildChannel();
return transportChannel.passTo(new DistributeInputConsumer(isFlush, channelList));
}
@Nonnull
private static OutputChannel> join(final boolean isFlush,
@Nonnull final List extends OutputChannel extends OUTPUT>> channels) {
final int size = channels.size();
if (size == 0) {
throw new IllegalArgumentException("the list of channels must not be empty");
}
final TransportChannel> transportChannel = JRoutine.transport().buildChannel();
final JoinOutputConsumer consumer =
new JoinOutputConsumer(transportChannel, size, isFlush);
merge(channels).passTo(consumer);
return transportChannel;
}
@Nonnull
@SuppressWarnings("unchecked")
private static OutputChannel> join(final boolean isFlush,
@Nonnull final OutputChannel>... channels) {
final int length = channels.length;
if (length == 0) {
throw new IllegalArgumentException("the array of channels must not be empty");
}
final TransportChannel> transportChannel = JRoutine.transport().buildChannel();
final JoinOutputConsumer consumer =
new JoinOutputConsumer(transportChannel, length, isFlush);
merge(channels).passTo(consumer);
return transportChannel;
}
/**
* Data class storing information about the origin of the data.
*
* @param the data type.
*/
@SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD",
justification = "this is an immutable data class")
public static class Selectable {
/**
* The data object.
*/
public final DATA data;
/**
* The origin channel index.
*/
public final int index;
/**
* Constructor.
*
* @param data the data object.
* @param index the channel index.
*/
public Selectable(final DATA data, final int index) {
this.data = data;
this.index = index;
}
/**
* Returns the data object casted to the specific type.
*
* @param the data type.
* @return the data object.
*/
@SuppressWarnings("unchecked")
public TYPE data() {
return (TYPE) data;
}
@Override
public int hashCode() {
// auto-generated code
int result = data != null ? data.hashCode() : 0;
result = 31 * result + index;
return result;
}
@Override
public boolean equals(final Object o) {
// auto-generated code
if (this == o) {
return true;
}
if (!(o instanceof Selectable)) {
return false;
}
final Selectable> that = (Selectable>) o;
return index == that.index && !(data != null ? !data.equals(that.data)
: that.data != null);
}
}
/**
* Output consumer distributing list of inputs among a list of input channels.
*/
private static class DistributeInputConsumer extends TemplateOutputConsumer> {
private final ArrayList> mChannels;
private final boolean mIsFlush;
/**
* Constructor.
*
* @param isFlush whether the inputs have to be flushed.
* @param channels the list of channels.
*/
private DistributeInputConsumer(final boolean isFlush,
@Nonnull final ArrayList> channels) {
mChannels = channels;
mIsFlush = isFlush;
}
@Override
public void onError(@Nullable final RoutineException error) {
for (final InputChannel> channel : mChannels) {
channel.abort(error);
}
}
@Override
@SuppressWarnings("unchecked")
public void onOutput(final List> inputs) {
final int inputSize = inputs.size();
final ArrayList> channels = mChannels;
final int size = channels.size();
if (inputSize > size) {
throw new IllegalArgumentException();
}
final boolean isFlush = mIsFlush;
for (int i = 0; i < size; i++) {
final InputChannel channel = (InputChannel) channels.get(i);
if (i < inputSize) {
channel.pass(inputs.get(i));
} else if (isFlush) {
channel.pass((Object) null);
}
}
}
}
/**
* Output consumer filtering selectable input data.
*
* @param the input data type.
*/
private static class FilterInputConsumer
extends TemplateOutputConsumer> {
private final int mIndex;
private final InputChannel super INPUT> mInputChannel;
/**
* Constructor.
*
* @param inputChannel the input channel to feed.
* @param index the index to filter.
*/
private FilterInputConsumer(@Nonnull final InputChannel super INPUT> inputChannel,
final int index) {
mInputChannel = inputChannel;
mIndex = index;
}
@Override
public void onError(@Nullable final RoutineException error) {
mInputChannel.abort(error);
}
@Override
public void onOutput(final Selectable selectable) {
if (selectable.index == mIndex) {
mInputChannel.pass(selectable.data);
}
}
}
/**
* Output consumer filtering selectable output data.
*
* @param the output data type.
*/
private static class FilterOutputConsumer
implements OutputConsumer> {
private final int mIndex;
private final TransportChannel mInputChannel;
/**
* Constructor.
*
* @param inputChannel the transport channel.
* @param index the index to filter.
*/
private FilterOutputConsumer(@Nonnull final TransportChannel inputChannel,
final int index) {
mInputChannel = inputChannel;
mIndex = index;
}
public void onComplete() {
mInputChannel.close();
}
public void onError(@Nullable final RoutineException error) {
mInputChannel.abort(error);
}
public void onOutput(final Selectable extends OUTPUT> selectable) {
if (selectable.index == mIndex) {
mInputChannel.pass(selectable.data);
}
}
}
/**
* Output consumer joining the data coming from several channels.
*
* @param the output data type.
*/
private static class JoinOutputConsumer implements OutputConsumer> {
protected final TransportChannel> mInputChannel;
protected final SimpleQueue[] mQueues;
private final boolean mIsFlush;
/**
* Constructor.
*
* @param inputChannel the transport channel.
* @param size the number of channels to join.
* @param isFlush whether the inputs have to be flushed.
*/
@SuppressWarnings("unchecked")
private JoinOutputConsumer(@Nonnull final TransportChannel> inputChannel,
final int size, final boolean isFlush) {
final SimpleQueue[] queues = (mQueues = new SimpleQueue[size]);
mInputChannel = inputChannel;
mIsFlush = isFlush;
for (int i = 0; i < size; i++) {
queues[i] = new SimpleQueue();
}
}
protected void flush() {
final TransportChannel> inputChannel = mInputChannel;
final SimpleQueue[] queues = mQueues;
final int length = queues.length;
final ArrayList outputs = new ArrayList(length);
boolean isEmpty;
do {
isEmpty = true;
for (final SimpleQueue queue : queues) {
if (!queue.isEmpty()) {
isEmpty = false;
outputs.add(queue.removeFirst());
} else {
outputs.add(null);
}
}
if (!isEmpty) {
inputChannel.pass(outputs);
outputs.clear();
} else {
break;
}
} while (true);
}
public void onComplete() {
if (mIsFlush) {
flush();
}
mInputChannel.close();
}
public void onError(@Nullable final RoutineException error) {
mInputChannel.abort(error);
}
@SuppressWarnings("unchecked")
public void onOutput(final Selectable selectable) {
final int index = selectable.index;
final SimpleQueue[] queues = mQueues;
queues[index].add(selectable.data);
final int length = queues.length;
boolean isFull = true;
for (final SimpleQueue queue : queues) {
if (queue.isEmpty()) {
isFull = false;
break;
}
}
if (isFull) {
final ArrayList outputs = new ArrayList(length);
for (final SimpleQueue queue : queues) {
outputs.add(queue.removeFirst());
}
mInputChannel.pass(outputs);
}
}
}
/**
* Output consumer transforming input data into selectable ones.
*
* @param the channel data type.
* @param the input data type.
*/
private static class SelectableInputConsumer
extends TemplateOutputConsumer {
private final int mIndex;
private final InputChannel super Selectable> mInputChannel;
/**
* Constructor.
*
* @param inputChannel the selectable channel.
* @param index the selectable index.
*/
private SelectableInputConsumer(
@Nonnull final InputChannel super Selectable> inputChannel,
final int index) {
mInputChannel = inputChannel;
mIndex = index;
}
@Override
public void onError(@Nullable final RoutineException error) {
mInputChannel.abort(error);
}
@Override
public void onOutput(final INPUT input) {
mInputChannel.pass(new Selectable(input, mIndex));
}
}
/**
* Output consumer transforming output data into selectable ones.
*
* @param the output data type.
*/
private static class SelectableOutputConsumer implements OutputConsumer {
private final int mIndex;
private final TransportChannel> mInputChannel;
/**
* Constructor.
*
* @param inputChannel the transport channel.
* @param index the selectable index.
*/
private SelectableOutputConsumer(
@Nonnull final TransportChannel> inputChannel, final int index) {
mInputChannel = inputChannel;
mIndex = index;
}
public void onComplete() {
mInputChannel.close();
}
public void onError(@Nullable final RoutineException error) {
mInputChannel.abort(error);
}
public void onOutput(final OUTPUT output) {
mInputChannel.pass(new Selectable(output, mIndex));
}
}
/**
* Output consumer sorting selectable inputs among a list of input channels.
*/
private static class SortingInputConsumer extends TemplateOutputConsumer> {
private final ArrayList> mChannelList;
private final int mSize;
private final int mStartIndex;
/**
* Constructor.
*
* @param startIndex the selectable start index.
* @param channels the list of channels.
*/
private SortingInputConsumer(final int startIndex,
@Nonnull final ArrayList> channels) {
mStartIndex = startIndex;
mChannelList = channels;
mSize = channels.size();
}
@Override
public void onError(@Nullable final RoutineException error) {
for (final InputChannel> inputChannel : mChannelList) {
inputChannel.abort(error);
}
}
@Override
@SuppressWarnings("unchecked")
public void onOutput(final Selectable> selectable) {
final int index = selectable.index - mStartIndex;
if ((index < 0) || (index >= mSize)) {
return;
}
final InputChannel inputChannel =
(InputChannel) mChannelList.get(index);
if (inputChannel != null) {
inputChannel.pass(selectable.data);
}
}
}
/**
* Output consumer sorting selectable inputs among a map of input channels.
*/
private static class SortingInputMapConsumer extends TemplateOutputConsumer> {
private final HashMap> mChannelMap;
/**
* Constructor.
*
* @param channelMap the map of indexes and input channels.
*/
private SortingInputMapConsumer(
@Nonnull final HashMap> channelMap) {
mChannelMap = channelMap;
}
@Override
public void onError(@Nullable final RoutineException error) {
for (final InputChannel> inputChannel : mChannelMap.values()) {
inputChannel.abort(error);
}
}
@Override
@SuppressWarnings("unchecked")
public void onOutput(final Selectable> selectable) {
final InputChannel inputChannel =
(InputChannel) mChannelMap.get(selectable.index);
if (inputChannel != null) {
inputChannel.pass(selectable.data);
}
}
}
/**
* Output consumer sorting the output data among a map of output channels.
*
* @param the output data type.
*/
private static class SortingOutputConsumer
implements OutputConsumer> {
private final HashMap> mChannelMap;
/**
* Constructor.
*
* @param channelMap the map of indexes and transport channels.
*/
private SortingOutputConsumer(
@Nonnull final HashMap> channelMap) {
mChannelMap = channelMap;
}
public void onComplete() {
for (final TransportChannel channel : mChannelMap.values()) {
channel.close();
}
}
public void onError(@Nullable final RoutineException error) {
for (final TransportChannel channel : mChannelMap.values()) {
channel.abort(error);
}
}
public void onOutput(final Selectable extends OUTPUT> selectable) {
final TransportChannel channel = mChannelMap.get(selectable.index);
if (channel != null) {
channel.pass(selectable.data);
}
}
}
}