PageRenderTime 28ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/groovy/groovyx/gpars/dataflow/stream/StreamCore.java

http://github.com/vaclav/GPars
Java | 375 lines | 213 code | 41 blank | 121 comment | 23 complexity | 024148121b1322726df77ca2779d7932 MD5 | raw file
  1. // GPars - Groovy Parallel Systems
  2. //
  3. // Copyright © 2008-2012 The original author or authors
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. package groovyx.gpars.dataflow.stream;
  17. import groovy.lang.Closure;
  18. import groovyx.gpars.actor.impl.MessageStream;
  19. import groovyx.gpars.dataflow.DataCallback;
  20. import groovyx.gpars.dataflow.Dataflow;
  21. import groovyx.gpars.dataflow.DataflowChannelListener;
  22. import groovyx.gpars.dataflow.DataflowReadChannel;
  23. import groovyx.gpars.dataflow.DataflowVariable;
  24. import groovyx.gpars.dataflow.expression.DataflowExpression;
  25. import java.util.Collection;
  26. import java.util.Iterator;
  27. import java.util.concurrent.CopyOnWriteArrayList;
  28. import java.util.concurrent.atomic.AtomicReference;
  29. /**
  30. * Represents a common base for publish-subscribe deterministic channels.
  31. *
  32. * @param <T> Type for values to pass through the channels
  33. * @author Johannes Link, Vaclav Pech
  34. */
  35. public abstract class StreamCore<T> implements FList<T> {
  36. protected final DataflowVariable<T> first;
  37. protected final AtomicReference<StreamCore<T>> rest = new AtomicReference<StreamCore<T>>();
  38. /**
  39. * A collection of listeners who need to be informed each time the stream is bound to a value
  40. */
  41. protected final Collection<MessageStream> wheneverBoundListeners;
  42. /**
  43. * Creates an empty stream
  44. *
  45. * @param first The variable to store as the head of the stream
  46. */
  47. protected StreamCore(final DataflowVariable<T> first) {
  48. this.first = first;
  49. wheneverBoundListeners = new CopyOnWriteArrayList<MessageStream>();
  50. }
  51. /**
  52. * Creates a stream while applying the supplied initialization closure to it
  53. *
  54. * @param first The variable to store as the head of the stream
  55. * @param toBeApplied The closure to use for initialization
  56. */
  57. protected StreamCore(final DataflowVariable<T> first, final Closure toBeApplied) {
  58. this(first);
  59. apply(toBeApplied);
  60. }
  61. @SuppressWarnings({"AssignmentToCollectionOrArrayFieldFromParameter"})
  62. protected StreamCore(final DataflowVariable<T> first, final Collection<MessageStream> wheneverBoundListeners, final Collection<DataflowChannelListener<T>> updateListeners) {
  63. this.first = first;
  64. this.wheneverBoundListeners = wheneverBoundListeners;
  65. hookWheneverBoundListeners(first);
  66. addUpdateListeners(updateListeners);
  67. }
  68. private void addUpdateListeners(final Collection<DataflowChannelListener<T>> updateListeners) {
  69. first.getEventManager().addAllDataflowChannelListeners(updateListeners);
  70. }
  71. final void addUpdateListener(final DataflowChannelListener<T> updateListener) {
  72. first.getEventManager().addDataflowChannelListener(updateListener);
  73. }
  74. public static <T> T eos() {
  75. return null;
  76. }
  77. private static <T> T eval(final Object valueOrDataflowVariable) {
  78. if (valueOrDataflowVariable instanceof DataflowVariable)
  79. try {
  80. return ((DataflowReadChannel<T>) valueOrDataflowVariable).getVal();
  81. } catch (InterruptedException e) {
  82. throw new RuntimeException(e);
  83. }
  84. return (T) valueOrDataflowVariable;
  85. }
  86. /**
  87. * Populates the stream with generated values
  88. *
  89. * @param seed The initial element to evaluate and add as the first value of the stream
  90. * @param generator A closure generating stream elements from the previous values
  91. * @param condition A closure indicating whether the generation should continue based on the last generated value
  92. * @return This stream
  93. */
  94. public final StreamCore<T> generate(final T seed, final Closure generator, final Closure condition) {
  95. generateNext(seed, this, generator, condition);
  96. return this;
  97. }
  98. private void generateNext(final T value, final StreamCore<T> stream, final Closure generator, final Closure condition) {
  99. T recurValue = value;
  100. StreamCore<T> recurStream = stream;
  101. while (true) {
  102. final boolean addValue = (Boolean) condition.call(new Object[]{recurValue});
  103. if (!addValue) {
  104. recurStream.leftShift(StreamCore.<T>eos());
  105. return;
  106. }
  107. recurStream = recurStream.leftShift(recurValue);
  108. recurValue = (T) eval(generator.call(new Object[]{recurValue}));
  109. }
  110. }
  111. /**
  112. * Calls the supplied closure with the stream as a parameter
  113. *
  114. * @param closure The closure to call
  115. * @return This instance of DataflowStream
  116. */
  117. public final StreamCore<T> apply(final Closure closure) {
  118. closure.call(new Object[]{this});
  119. return this;
  120. }
  121. /**
  122. * Adds a dataflow variable value to the stream, once the value is available
  123. *
  124. * @param ref The DataflowVariable to check for value
  125. * @return The rest of the stream
  126. */
  127. public final StreamCore<T> leftShift(final DataflowReadChannel<T> ref) {
  128. ref.getValAsync(new MessageStream() {
  129. @Override
  130. public MessageStream send(final Object message) {
  131. first.bind((T) message);
  132. return null;
  133. }
  134. });
  135. return (StreamCore<T>) getRest();
  136. }
  137. /**
  138. * Adds a value to the stream
  139. *
  140. * @param value The value to add
  141. * @return The rest of the stream
  142. */
  143. public final StreamCore<T> leftShift(final T value) {
  144. bind(value);
  145. return (StreamCore<T>) getRest();
  146. }
  147. private void bind(final T value) {
  148. first.bind(value);
  149. }
  150. final DataflowVariable<T> getFirstDFV() {
  151. return first;
  152. }
  153. /**
  154. * Retrieved the first element in the stream, blocking until a value is available
  155. *
  156. * @return The first item in the stream
  157. */
  158. @Override
  159. public final T getFirst() {
  160. try {
  161. return first.getVal();
  162. } catch (InterruptedException e) {
  163. throw new RuntimeException(e);
  164. }
  165. }
  166. /**
  167. * Retrieves a DataflowStream representing the rest of this Stream after removing the first element
  168. *
  169. * @return The remaining stream elements
  170. */
  171. @Override
  172. public abstract FList<T> getRest();
  173. /**
  174. * Indicates, whether the first element in the stream is an eos
  175. */
  176. @Override
  177. public final boolean isEmpty() {
  178. return getFirst() == eos();
  179. }
  180. /**
  181. * Builds a filtered stream using the supplied filter closure
  182. *
  183. * @param filterClosure The closure to decide on inclusion of elements
  184. * @return The first item of the filtered stream
  185. */
  186. @Override
  187. public final FList<T> filter(final Closure filterClosure) {
  188. final StreamCore<T> newStream = createNewStream();
  189. filter(this, filterClosure, newStream);
  190. return newStream;
  191. }
  192. private void filter(final StreamCore<T> rest, final Closure filterClosure, final StreamCore<T> result) {
  193. StreamCore<T> recurRest = rest;
  194. StreamCore<T> recurResult = result;
  195. while (true) {
  196. if (recurRest.isEmpty()) {
  197. recurResult.leftShift(StreamCore.<T>eos());
  198. return;
  199. }
  200. final boolean include = (Boolean) eval(filterClosure.call(new Object[]{recurRest.getFirst()}));
  201. if (include) recurResult = recurResult.leftShift(recurRest.getFirst());
  202. recurRest = (StreamCore<T>) recurRest.getRest();
  203. }
  204. }
  205. /**
  206. * Builds a modified stream using the supplied map closure
  207. *
  208. * @param mapClosure The closure to transform elements
  209. * @return The first item of the transformed stream
  210. */
  211. @Override
  212. public final FList<Object> map(final Closure mapClosure) {
  213. final StreamCore<Object> newStream = (StreamCore<Object>) createNewStream();
  214. map(this, mapClosure, newStream);
  215. return newStream;
  216. }
  217. private void map(final FList<T> rest, final Closure mapClosure, final StreamCore<Object> result) {
  218. FList<T> recurRest = rest;
  219. StreamCore<Object> recurResult = result;
  220. while (true) {
  221. if (recurRest.isEmpty()) {
  222. recurResult.leftShift((Object)StreamCore.eos()); // explicit casting to make tests pass (Java8 related)
  223. return;
  224. }
  225. final Object mapped = mapClosure.call(new Object[]{recurRest.getFirst()});
  226. recurResult = recurResult.leftShift((Object)eval(mapped)); // explicit casting to make tests pass (Java8 related)
  227. recurRest = recurRest.getRest();
  228. }
  229. }
  230. /**
  231. * Reduces all elements in the stream using the supplied closure
  232. *
  233. * @param reduceClosure The closure to reduce elements of the stream gradually into an accumulator. The accumulator is seeded with the first stream element.
  234. * @return The result of reduction of the whole stream
  235. */
  236. @Override
  237. public final T reduce(final Closure reduceClosure) {
  238. if (isEmpty())
  239. return null;
  240. return reduce(getFirst(), getRest(), reduceClosure);
  241. }
  242. /**
  243. * Reduces all elements in the stream using the supplied closure
  244. *
  245. * @param reduceClosure The closure to reduce elements of the stream gradually into an accumulator.
  246. * @param seed The value to initialize the accumulator with.
  247. * @return The result of reduction of the whole stream
  248. */
  249. @Override
  250. public final T reduce(final T seed, final Closure reduceClosure) {
  251. return reduce(seed, this, reduceClosure);
  252. }
  253. private T reduce(final T current, final FList<T> rest, final Closure reduceClosure) {
  254. T recurCurrent = current;
  255. FList<T> recurRest = rest;
  256. while (true) {
  257. if (recurRest.isEmpty())
  258. return recurCurrent;
  259. final Object aggregate = reduceClosure.call(new Object[]{recurCurrent, recurRest.getFirst()});
  260. recurCurrent = (T) eval(aggregate);
  261. recurRest = recurRest.getRest();
  262. }
  263. }
  264. /**
  265. * Builds an iterator to iterate over the stream
  266. *
  267. * @return A new FListIterator instance
  268. */
  269. @Override
  270. public final Iterator<T> iterator() {
  271. return new FListIterator<T>(this);
  272. }
  273. @Override
  274. public String appendingString() {
  275. if (!first.isBound())
  276. return ", ?";
  277. if (isEmpty())
  278. return "";
  279. return ", " + getFirst() + getRest().appendingString();
  280. }
  281. @Override
  282. public boolean equals(final Object obj) {
  283. if (this == obj) return true;
  284. if (obj == null || getClass() != obj.getClass()) return false;
  285. final FList stream = (FList) obj;
  286. if (isEmpty())
  287. return stream.isEmpty();
  288. if (!getFirst().equals(stream.getFirst()))
  289. return false;
  290. return getRest().equals(stream.getRest());
  291. }
  292. @Override
  293. public int hashCode() {
  294. int result = first.hashCode();
  295. result = 31 * result + rest.hashCode();
  296. return result;
  297. }
  298. /**
  299. * A factory method to create new instances of the correct class when needed
  300. *
  301. * @return An instance of the appropriate sub-class
  302. */
  303. protected abstract StreamCore<T> createNewStream();
  304. public final void wheneverBound(final Closure closure) {
  305. wheneverBoundListeners.add(new DataCallback(closure, Dataflow.retrieveCurrentDFPGroup()));
  306. first.whenBound(closure);
  307. }
  308. public final void wheneverBound(final MessageStream stream) {
  309. wheneverBoundListeners.add(stream);
  310. first.whenBound(stream);
  311. }
  312. /**
  313. * Hooks the registered when bound handlers to the supplied dataflow expression
  314. *
  315. * @param expr The expression to hook all the when bound listeners to
  316. * @return The supplied expression handler to allow method chaining
  317. */
  318. private DataflowExpression<T> hookWheneverBoundListeners(final DataflowExpression<T> expr) {
  319. for (final MessageStream listener : wheneverBoundListeners) {
  320. expr.whenBound(listener);
  321. }
  322. return expr;
  323. }
  324. /**
  325. * Increases the number of parties required to perform the data exchange
  326. */
  327. public void incrementParties() {
  328. }
  329. /**
  330. * Decreases the number of parties required to perform the data exchange
  331. */
  332. public void decrementParties() {
  333. }
  334. }