/* * Copyright (C) 2015 SoftIndex LLC. * * 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 io.datakernel.stream.processor; import io.datakernel.eventloop.Eventloop; import io.datakernel.jmx.EventloopJmxMBean; import io.datakernel.jmx.JmxAttribute; import io.datakernel.stream.AbstractStreamTransformer_1_1; import io.datakernel.stream.StreamDataReceiver; import static com.google.common.base.Preconditions.checkNotNull; /** * Provides to create some MapperProjection which will change received data, and send it to the * destination. It is {@link AbstractStreamTransformer_1_1} which receives original * data and streams changed data. * * @param <I> type of input data * @param <O> type of output data */ public final class StreamMap<I, O> extends AbstractStreamTransformer_1_1<I, O> implements EventloopJmxMBean { private int jmxItems; private final InputConsumer inputConsumer; private final OutputProducer outputProducer; // region creators private StreamMap(Eventloop eventloop, Mapper<I, O> mapper) { super(eventloop); this.inputConsumer = new InputConsumer(); this.outputProducer = new OutputProducer(mapper); } @Override protected AbstractInputConsumer getInputImpl() { return inputConsumer; } @Override protected AbstractOutputProducer getOutputImpl() { return outputProducer; } public static <I, O> StreamMap<I, O> create(Eventloop eventloop, Mapper<I, O> mapper) { return new StreamMap<I, O>(eventloop, mapper); } // endregion /** * Primary interface which does mapping * * @param <I> type of input data * @param <O> type of output data */ public interface Mapper<I, O> { /** * Holds mapping and streams it to destination * * @param input received item * @param output callback for streaming it to destination */ void map(I input, StreamDataReceiver<O> output); } /** * Map data with method apply and sends it to the destination * * @param <I> type of input data * @param <O> type of output data */ public static abstract class MapperProjection<I, O> implements Mapper<I, O> { /** * It contains operations which will be done for mapping * * @param input received data * @return mapped object */ protected abstract O apply(I input); @Override public final void map(I input, StreamDataReceiver<O> output) { O result = apply(input); output.onData(result); } } /** * Filter which can map only that data which returns true in the method apply(), and sends it * to the destination * * @param <I> type of input data */ public static abstract class MapperFilter<I> implements Mapper<I, I> { /** * It contains some boolean expression. It resolves or does not resolves sending * this data to destination * * @param input received data * @return true, if this item must be sending, false else */ protected abstract boolean apply(I input); @Override public final void map(I input, StreamDataReceiver<I> output) { if (apply(input)) { output.onData(input); } } } /** * Provides use two mappers in one time. * * @param <I> type of input data for first mapper * @param <T> type of output data for first mapper if type of input data for second filter * @param <O> type of output data of second filter * @return new mapper which is composition from two mappers from arguments */ public static <I, T, O> Mapper<I, O> combine(final Mapper<I, T> mapper1, final Mapper<T, O> mapper2) { return new Mapper<I, O>() { @Override public void map(I input, final StreamDataReceiver<O> output) { mapper1.map(input, new StreamDataReceiver<T>() { @Override public void onData(T item) { mapper2.map(item, output); } }); } }; } protected final class InputConsumer extends AbstractInputConsumer { @Override protected void onUpstreamEndOfStream() { outputProducer.sendEndOfStream(); } @SuppressWarnings("unchecked") @Override public StreamDataReceiver<I> getDataReceiver() { return outputProducer; } } protected final class OutputProducer extends AbstractOutputProducer implements StreamDataReceiver<I> { private final Mapper<I, O> mapper; public OutputProducer(Mapper<I, O> mapper) {this.mapper = checkNotNull(mapper);} @Override protected void onDownstreamSuspended() { inputConsumer.suspend(); } @Override protected void onDownstreamResumed() { inputConsumer.resume(); } @SuppressWarnings("AssertWithSideEffects") @Override public void onData(I item) { assert jmxItems != ++jmxItems; mapper.map(item, downstreamDataReceiver); } } // jmx @Override public Eventloop getEventloop() { return eventloop; } @JmxAttribute public int getItems() { return jmxItems; } @SuppressWarnings("AssertWithSideEffects") @Override public String toString() { String items = "?"; assert (items = "" + jmxItems) != null; return '{' + super.toString() + " items:" + items + '}'; } }