All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datakernel.stream.processor.StreamReducers Maven / Gradle / Ivy

Go to download

Composable asynchronous/reactive streams with powerful data processing capabilities.

The newest version!
/*
 * 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.stream.StreamDataReceiver;

/**
 * Static utility methods pertaining to {@link StreamReducers.Reducer}.
 * Contains primary ready for use reducers.
 */
public class StreamReducers {
	private StreamReducers() {
	}

	/**
	 * Returns reducer which streams only one element from group of same keys.
	 *
	 * @param  type of key
	 * @param  type of output
	 */
	public static  Reducer mergeDeduplicateReducer() {
		return new MergeDeduplicateReducer<>();
	}

	/**
	 * Returns reducer which streams all receives elements sorted by keys.
	 *
	 * @param  type of key
	 * @param  type of output
	 */
	public static  Reducer mergeSortReducer() {
		return new MergeSortReducer<>();
	}

	/**
	 * It is primary interface of Reducer.
	 *
	 * @param  type of keys
	 * @param  type of input data
	 * @param  type of output data
	 * @param  type of accumulator
	 */
	public interface Reducer {
		/**
		 * Run when reducer received first element with key from argument.
		 *
		 * @param stream     stream where to send result
		 * @param key        key of element
		 * @param firstValue received value
		 * @return accumulator for further operations
		 */
		A onFirstItem(StreamDataReceiver stream, K key, I firstValue);

		/**
		 * Run when reducer received each next element with key from argument
		 *
		 * @param stream      stream where to send result
		 * @param key         key of element
		 * @param nextValue   received value
		 * @param accumulator accumulator which contains results of all previous operations
		 * @return accumulator for further operations
		 */
		A onNextItem(StreamDataReceiver stream, K key, I nextValue, A accumulator);

		/**
		 * Run after receiving last element with key from argument
		 *
		 * @param stream      stream where to send result
		 * @param key         key of element
		 * @param accumulator accumulator which contains results of all previous operations
		 */
		void onComplete(StreamDataReceiver stream, K key, A accumulator);
	}

	/**
	 * Represents a helpful class which contains methods for simple casting types of input and
	 * output streams
	 *
	 * @param  type of keys
	 * @param  type of input data
	 * @param  type of output data
	 * @param  type of accumulator
	 */
	public abstract static class ReducerToResult {

		/**
		 * Creates a new accumulator with key from argument
		 *
		 * @param key key for new accumulator
		 * @return new accumulator
		 */
		public abstract A createAccumulator(K key);

		/**
		 * Processing value with accumulator
		 *
		 * @param accumulator accumulator with partials results
		 * @param value       received value for accumulating
		 * @return changing accumulator
		 */
		public abstract A accumulate(A accumulator, I value);

		/**
		 * Combines two accumulators from argument.
		 *
		 * @return new accumulator
		 */
		public A combine(A accumulator, A anotherAccumulator) {
			throw new UnsupportedOperationException("can not combine two accumulators");
		}

		/**
		 * Calls after completing receiving results for some key. It processed
		 * obtained accumulator and returns stream of output type from generic
		 *
		 * @param accumulator obtained accumulator after end receiving
		 * @return stream of output type from generic
		 */
		public abstract O produceResult(A accumulator);

		/**
		 * Creates a new reducer which receives items,accumulated it, produces after end of stream
		 * and streams result
		 */
		public final Reducer inputToOutput() {
			return new InputToOutput<>(this);
		}

		/**
		 * Creates a new reducer which receives items,accumulated it and streams obtained accumulator
		 */
		public final Reducer inputToAccumulator() {
			return new InputToAccumulator<>(this);
		}

		/**
		 * Creates a new reducer which receives accumulators,combines it, produces after end of stream
		 * and streams result
		 */
		public final Reducer accumulatorToOutput() {
			return new AccumulatorToOutput<>(this);
		}

		/**
		 * Creates a new reducer which receives accumulators,combines it, and streams obtained accumulator
		 */
		public final Reducer accumulatorToAccumulator() {
			return new AccumulatorToAccumulator<>(this);
		}

		/**
		 * Represents a reducer which contains ReducerToResult where identified methods for processing
		 * items . After searching accumulator performs some action with it with method produceResult
		 * from ReducerToResult.
		 *
		 * @param  type of keys
		 * @param  type of input data
		 * @param  type of output data
		 * @param  type of accumulator
		 */
		public static final class InputToOutput implements Reducer {
			private ReducerToResult reducerToResult;

			/**
			 * Creates a new instance of InputToOutput with  ReducerToResult from arguments
			 */
			public InputToOutput(ReducerToResult reducerToResult) {
				this.reducerToResult = reducerToResult;
			}

			/**
			 * Creates accumulator with ReducerToResult and accumulates with it first item
			 *
			 * @param stream     stream where to send result
			 * @param key        key of element
			 * @param firstValue received value
			 * @return accumulator with result
			 */
			@Override
			public final A onFirstItem(StreamDataReceiver stream, K key, I firstValue) {
				A accumulator = reducerToResult.createAccumulator(key);
				return reducerToResult.accumulate(accumulator, firstValue);
			}

			/**
			 * Accumulates each next element.
			 *
			 * @param stream      stream where to send result
			 * @param key         key of element
			 * @param nextValue   received value
			 * @param accumulator accumulator which contains results of all previous operations
			 * @return accumulator with result
			 */
			@Override
			public final A onNextItem(StreamDataReceiver stream, K key, I nextValue, A accumulator) {
				return reducerToResult.accumulate(accumulator, nextValue);
			}

			/**
			 * Produces result accumulator with ReducerToResult and streams it
			 *
			 * @param stream      stream where to send result
			 * @param key         key of element
			 * @param accumulator accumulator which contains results of all previous operations
			 */
			@Override
			public final void onComplete(StreamDataReceiver stream, K key, A accumulator) {
				stream.onData(reducerToResult.produceResult(accumulator));
			}
		}

		/**
		 * Represents a reducer which contains ReducerToResult where identified methods for processing
		 * items . Streams obtained accumulator on complete.
		 *
		 * @param  type of keys
		 * @param  type of input data
		 * @param  type of output data
		 * @param  type of accumulator
		 */
		public static final class InputToAccumulator implements Reducer {
			private ReducerToResult reducerToResult;

			/**
			 * Creates a new instance of InputToAccumulator with ReducerToResult from argument
			 */
			public InputToAccumulator(ReducerToResult reducerToResult) {
				this.reducerToResult = reducerToResult;
			}

			/**
			 * Creates accumulator with ReducerToResult and accumulates with it first item
			 *
			 * @param stream     stream where to send result
			 * @param key        key of element
			 * @param firstValue received value
			 * @return accumulator with result
			 */
			@Override
			public A onFirstItem(StreamDataReceiver stream, K key, I firstValue) {
				A accumulator = reducerToResult.createAccumulator(key);
				return reducerToResult.accumulate(accumulator, firstValue);
			}

			/**
			 * Accumulates each next element.
			 *
			 * @param stream      stream where to send result
			 * @param key         key of element
			 * @param nextValue   received value
			 * @param accumulator accumulator which contains results of all previous operations
			 * @return accumulator with result
			 */
			@Override
			public A onNextItem(StreamDataReceiver stream, K key, I nextValue, A accumulator) {
				return reducerToResult.accumulate(accumulator, nextValue);
			}

			/**
			 * Streams obtained accumulator
			 *
			 * @param stream      stream where to send result
			 * @param key         key of element
			 * @param accumulator accumulator which contains results of all previous operations
			 */
			@Override
			public void onComplete(StreamDataReceiver stream, K key, A accumulator) {
				stream.onData(accumulator);
			}
		}

		/**
		 * Represents a reducer which contains ReducerToResult where identified methods for processing
		 * items . Each received item is accumulator and it combines with previous value.  After
		 * searching accumulator performs some action with it with method produceResult from
		 * ReducerToResult.
		 *
		 * @param  type of keys
		 * @param  type of input data
		 * @param  type of output data
		 * @param  type of accumulator
		 */
		public static final class AccumulatorToOutput implements Reducer {
			private ReducerToResult reducerToResult;

			/**
			 * Creates a new instance of InputToAccumulator with ReducerToResult from argument
			 */
			public AccumulatorToOutput(ReducerToResult reducerToResult) {
				this.reducerToResult = reducerToResult;
			}

			/**
			 * Creates accumulator which is first item
			 *
			 * @param stream     stream where to send result
			 * @param key        key of element
			 * @param firstValue received value
			 * @return accumulator with result
			 */
			@Override
			public A onFirstItem(StreamDataReceiver stream, K key, A firstValue) {
				return firstValue;
			}

			/**
			 * Combines previous accumulator and each next item
			 *
			 * @param stream      stream where to send result
			 * @param key         key of element
			 * @param nextValue   received value
			 * @param accumulator accumulator which contains results of all previous combining
			 * @return accumulator with result
			 */
			@Override
			public A onNextItem(StreamDataReceiver stream, K key, A nextValue, A accumulator) {
				return reducerToResult.combine(accumulator, nextValue);
			}

			/**
			 * Produces result accumulator with ReducerToResult and streams it
			 *
			 * @param stream      stream where to send result
			 * @param key         key of element
			 * @param accumulator accumulator which contains results of all previous operations
			 */
			@Override
			public void onComplete(StreamDataReceiver stream, K key, A accumulator) {
				stream.onData(reducerToResult.produceResult(accumulator));
			}
		}

		/**
		 * Represents  a reducer which contains ReducerToResult where identified methods for processing
		 * items . Each received item is accumulator and it combines with previous value. Streams
		 * obtained accumulator on complete.
		 *
		 * @param  type of keys
		 * @param  type of input data
		 * @param  type of output data
		 * @param  type of accumulator
		 */
		public static final class AccumulatorToAccumulator implements Reducer {
			private ReducerToResult reducerToResult;

			public AccumulatorToAccumulator(ReducerToResult reducerToResult) {
				this.reducerToResult = reducerToResult;
			}

			/**
			 * Creates accumulator which is first item
			 *
			 * @param stream     stream where to send result
			 * @param key        key of element
			 * @param firstValue received value
			 * @return accumulator with result
			 */
			@Override
			public A onFirstItem(StreamDataReceiver stream, K key, A firstValue) {
				return firstValue;
			}

			/**
			 * Combines previous accumulator and each next item
			 *
			 * @param stream      stream where to send result
			 * @param key         key of element
			 * @param nextValue   received value
			 * @param accumulator accumulator which contains results of all previous combining
			 * @return accumulator with result
			 */
			@Override
			public A onNextItem(StreamDataReceiver stream, K key, A nextValue, A accumulator) {
				return reducerToResult.combine(accumulator, nextValue);
			}

			/**
			 * Streams obtained accumulator
			 *
			 * @param stream      stream where to send result
			 * @param key         key of element
			 * @param accumulator accumulator which contains results of all previous operations
			 */
			@Override
			public void onComplete(StreamDataReceiver stream, K key, A accumulator) {
				stream.onData(accumulator);
			}
		}
	}

	/**
	 * Represents a reducer which streams accumulator
	 *
	 * @param  type of keys
	 * @param  type of input data
	 * @param  type of accumulator and type of output data
	 */
	public abstract static class ReducerToAccumulator extends ReducerToResult {
		@Override
		public final A produceResult(A accumulator) {
			return accumulator;
		}
	}

	/**
	 * Represents a reducer which deduplicates items with same keys. Streams only one item with
	 * each key
	 *
	 * @param  type of keys
	 * @param  type of input and output data
	 */
	public static class MergeDeduplicateReducer implements Reducer {
		/**
		 * On first item with new key it streams it
		 *
		 * @param stream     stream where to send result
		 * @param key        key of element
		 * @param firstValue received value
		 */
		@Override
		public Void onFirstItem(StreamDataReceiver stream, K key, T firstValue) {
			stream.onData(firstValue);
			return null;
		}

		@Override
		public Void onNextItem(StreamDataReceiver stream, K key, T nextValue, Void accumulator) {
			return null;
		}

		@Override
		public void onComplete(StreamDataReceiver stream, K key, Void accumulator) {
		}
	}

	/**
	 * Represent a reducer which streams received items sorted by keys
	 *
	 * @param  type of keys
	 * @param  type of input and output data
	 */
	public static class MergeSortReducer implements Reducer {
		@Override
		public Void onFirstItem(StreamDataReceiver stream, K key, T firstValue) {
			stream.onData(firstValue);
			return null;
		}

		@Override
		public Void onNextItem(StreamDataReceiver stream, K key, T nextValue, Void accumulator) {
			stream.onData(nextValue);
			return null;
		}

		@Override
		public void onComplete(StreamDataReceiver stream, K key, Void accumulator) {
		}
	}
}