
groovyx.gpars.dataflow.stream.StreamCore Maven / Gradle / Ivy
// GPars - Groovy Parallel Systems
//
// Copyright © 2008-2012 The original author or authors
//
// 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 groovyx.gpars.dataflow.stream;
import groovy.lang.Closure;
import groovyx.gpars.actor.impl.MessageStream;
import groovyx.gpars.dataflow.DataCallback;
import groovyx.gpars.dataflow.Dataflow;
import groovyx.gpars.dataflow.DataflowChannelListener;
import groovyx.gpars.dataflow.DataflowReadChannel;
import groovyx.gpars.dataflow.DataflowVariable;
import groovyx.gpars.dataflow.expression.DataflowExpression;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicReference;
/**
* Represents a common base for publish-subscribe deterministic channels.
*
* @param Type for values to pass through the channels
* @author Johannes Link, Vaclav Pech
*/
public abstract class StreamCore implements FList {
protected final DataflowVariable first;
protected final AtomicReference> rest = new AtomicReference>();
/**
* A collection of listeners who need to be informed each time the stream is bound to a value
*/
protected final Collection wheneverBoundListeners;
/**
* Creates an empty stream
*
* @param first The variable to store as the head of the stream
*/
protected StreamCore(final DataflowVariable first) {
this.first = first;
wheneverBoundListeners = new CopyOnWriteArrayList();
}
/**
* Creates a stream while applying the supplied initialization closure to it
*
* @param first The variable to store as the head of the stream
* @param toBeApplied The closure to use for initialization
*/
protected StreamCore(final DataflowVariable first, final Closure toBeApplied) {
this(first);
apply(toBeApplied);
}
@SuppressWarnings({"AssignmentToCollectionOrArrayFieldFromParameter"})
protected StreamCore(final DataflowVariable first, final Collection wheneverBoundListeners, final Collection> updateListeners) {
this.first = first;
this.wheneverBoundListeners = wheneverBoundListeners;
hookWheneverBoundListeners(first);
addUpdateListeners(updateListeners);
}
private void addUpdateListeners(final Collection> updateListeners) {
first.getEventManager().addAllDataflowChannelListeners(updateListeners);
}
final void addUpdateListener(final DataflowChannelListener updateListener) {
first.getEventManager().addDataflowChannelListener(updateListener);
}
public static T eos() {
return null;
}
private static T eval(final Object valueOrDataflowVariable) {
if (valueOrDataflowVariable instanceof DataflowVariable)
try {
return ((DataflowReadChannel) valueOrDataflowVariable).getVal();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return (T) valueOrDataflowVariable;
}
/**
* Populates the stream with generated values
*
* @param seed The initial element to evaluate and add as the first value of the stream
* @param generator A closure generating stream elements from the previous values
* @param condition A closure indicating whether the generation should continue based on the last generated value
* @return This stream
*/
public final StreamCore generate(final T seed, final Closure generator, final Closure condition) {
generateNext(seed, this, generator, condition);
return this;
}
private void generateNext(final T value, final StreamCore stream, final Closure generator, final Closure condition) {
T recurValue = value;
StreamCore recurStream = stream;
while (true) {
final boolean addValue = (Boolean) condition.call(new Object[]{recurValue});
if (!addValue) {
recurStream.leftShift(StreamCore.eos());
return;
}
recurStream = recurStream.leftShift(recurValue);
recurValue = (T) eval(generator.call(new Object[]{recurValue}));
}
}
/**
* Calls the supplied closure with the stream as a parameter
*
* @param closure The closure to call
* @return This instance of DataflowStream
*/
public final StreamCore apply(final Closure closure) {
closure.call(new Object[]{this});
return this;
}
/**
* Adds a dataflow variable value to the stream, once the value is available
*
* @param ref The DataflowVariable to check for value
* @return The rest of the stream
*/
public final StreamCore leftShift(final DataflowReadChannel ref) {
ref.getValAsync(new MessageStream() {
@Override
public MessageStream send(final Object message) {
first.bind((T) message);
return null;
}
});
return (StreamCore) getRest();
}
/**
* Adds a value to the stream
*
* @param value The value to add
* @return The rest of the stream
*/
public final StreamCore leftShift(final T value) {
bind(value);
return (StreamCore) getRest();
}
private void bind(final T value) {
first.bind(value);
}
final DataflowVariable getFirstDFV() {
return first;
}
/**
* Retrieved the first element in the stream, blocking until a value is available
*
* @return The first item in the stream
*/
@Override
public final T getFirst() {
try {
return first.getVal();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
/**
* Retrieves a DataflowStream representing the rest of this Stream after removing the first element
*
* @return The remaining stream elements
*/
@Override
public abstract FList getRest();
/**
* Indicates, whether the first element in the stream is an eos
*/
@Override
public final boolean isEmpty() {
return getFirst() == eos();
}
/**
* Builds a filtered stream using the supplied filter closure
*
* @param filterClosure The closure to decide on inclusion of elements
* @return The first item of the filtered stream
*/
@Override
public final FList filter(final Closure filterClosure) {
final StreamCore newStream = createNewStream();
filter(this, filterClosure, newStream);
return newStream;
}
private void filter(final StreamCore rest, final Closure filterClosure, final StreamCore result) {
StreamCore recurRest = rest;
StreamCore recurResult = result;
while (true) {
if (recurRest.isEmpty()) {
recurResult.leftShift(StreamCore.eos());
return;
}
final boolean include = (Boolean) eval(filterClosure.call(new Object[]{recurRest.getFirst()}));
if (include) recurResult = recurResult.leftShift(recurRest.getFirst());
recurRest = (StreamCore) recurRest.getRest();
}
}
/**
* Builds a modified stream using the supplied map closure
*
* @param mapClosure The closure to transform elements
* @return The first item of the transformed stream
*/
@Override
public final FList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy