/*
* Copyright 2008-2016 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 com.nominanuda.zen.reactivestreams;
import static com.nominanuda.zen.common.Check.illegalargument;
import static com.nominanuda.zen.common.Check.illegalstate;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Supplier;
import org.reactivestreams.Processor;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import com.nominanuda.zen.stereotype.Factory;
@SuppressWarnings("unchecked")//TODO
public class Pipe<IN,OUT> {
public interface LeftClosed<X,T> extends Supplier<X>, Publisher<T> {
}
public interface RightClosed<IN,X> extends Supplier<X>, Subscriber<IN> {
}
public interface ClosedPipe<X,Y> {
X head();
Y tail();
}
private final boolean leftOpen;
private final boolean rightOpen;
private final List<Object> components;
private Pipe(List<Object> components, boolean leftOpen,boolean rightOpen) {
this.leftOpen = leftOpen;
this.rightOpen = rightOpen;
LinkedList<Object> l = new LinkedList<>(components);
this.components = Collections.unmodifiableList(l);
}
private Pipe(Object component, boolean leftOpen,boolean rightOpen) {
this.leftOpen = leftOpen;
this.rightOpen = rightOpen;
LinkedList<Object> l = new LinkedList<>();
l.add(component);
this.components = Collections.unmodifiableList(l);
}
private Pipe(Object component, List<Object> components, boolean leftOpen,boolean rightOpen) {
this.leftOpen = leftOpen;
this.rightOpen = rightOpen;
LinkedList<Object> l = new LinkedList<>();
l.add(component);
l.addAll(components);
this.components = Collections.unmodifiableList(l);
}
private Pipe(List<Object> components, Object component, boolean leftOpen,boolean rightOpen) {
this.leftOpen = leftOpen;
this.rightOpen = rightOpen;
LinkedList<Object> l = new LinkedList<>(components);
l.add(component);
this.components = Collections.unmodifiableList(l);
}
private Pipe(List<Object> components1, List<Object> components2, boolean leftOpen,boolean rightOpen) {
this.leftOpen = leftOpen;
this.rightOpen = rightOpen;
LinkedList<Object> l = new LinkedList<>(components1);
l.addAll(components2);
this.components = Collections.unmodifiableList(l);
}
public static <T> Pipe<Void,T> fromPublisher(Factory<? extends Publisher<? extends T>> pubFactory) {
return new Pipe<Void,T>(pubFactory, false, true);
}
public static <T> Pipe<Void,T> fromPublisher(Publisher<? extends T> pub) {
return new Pipe<Void,T>(pub, false, true);
}
public static <T> Pipe<T,Void> fromSubscriber(Factory<? extends Subscriber<? extends T>> subFactory) {
return new Pipe<T,Void>(subFactory, true, false);
}
public static <T> Pipe<T,Void> fromSubscriber(Subscriber<? extends T> sub) {
return new Pipe<T,Void>(sub, true, false);
}
public static <X,Y> Pipe<X,Y> fromProcessor(Factory<? extends Processor<? extends X, ? extends Y>> procFactory) {
return new Pipe<X,Y>(procFactory, true, true);
}
public static <X,Y> Pipe<X,Y> fromProcessor(Processor<? extends X, ? extends Y> procFactory) {
return new Pipe<X,Y>(procFactory, true, true);
}
public Pipe<Void,OUT> prependPublisher(Factory<? extends Publisher<? extends IN>> pubFactory) {
illegalargument.assertTrue(leftOpen, "pipe is left closed");
return new Pipe<Void, OUT>(pubFactory, components, false, rightOpen);
}
public Pipe<Void,OUT> prependPublisher(Publisher<? extends IN> pub) {
illegalargument.assertTrue(leftOpen, "pipe is left closed");
return new Pipe<Void, OUT>(pub, components, false, rightOpen);
}
public <T> Pipe<T,OUT> prependProcessor(Factory<? extends Processor<? extends T, ? extends IN>> procFactory) {
illegalargument.assertTrue(leftOpen, "pipe is left closed");
return new Pipe<T, OUT>(procFactory, components, true, rightOpen);
}
public <T> Pipe<T,OUT> prependProcessor(Processor<? extends T, ? extends IN> proc) {
illegalargument.assertTrue(leftOpen, "pipe is left closed");
return new Pipe<T, OUT>(proc, components, true, rightOpen);
}
public <T> Pipe<IN,T> appendProcessor(Factory<? extends Processor<? extends OUT,? extends T>> procFactory) {
illegalargument.assertTrue(rightOpen, "pipe is right closed");
return new Pipe<IN,T>(components, procFactory, leftOpen, true);
}
public <T> Pipe<IN,T> appendProcessor(Processor<? extends OUT,? extends T> proc) {
illegalargument.assertTrue(rightOpen, "pipe is right closed");
return new Pipe<IN,T>(components, proc, leftOpen, true);
}
public Pipe<IN,Void> appendSubscriber(Factory<? extends Subscriber<? extends OUT>> subFactory) {
illegalargument.assertTrue(rightOpen, "pipe is right closed");
return new Pipe<IN,Void>(components, subFactory, leftOpen, false);
}
public Pipe<IN,Void> appendSubscriber(Subscriber<? extends OUT> sub) {
illegalargument.assertTrue(rightOpen, "pipe is right closed");
return new Pipe<IN,Void>(components, sub, leftOpen, false);
}
public <T> Pipe<IN, T> appendPipe(Pipe<? extends OUT, T> p1) {
illegalargument.assertTrue(rightOpen, "pipe is right closed");
illegalargument.assertTrue(p1.leftOpen, "supplied pipe is left closed");
return new Pipe<IN, T>(components, p1.components, leftOpen, p1.rightOpen);
}
public <T> Pipe<T, OUT> prependPipe(Pipe<T, ? extends IN> p1) {
illegalargument.assertTrue(leftOpen, "pipe is left closed");
illegalargument.assertTrue(p1.rightOpen, "supplied pipe is right closed");
return new Pipe<T, OUT>(p1, components, p1.leftOpen, rightOpen);
}
public boolean isRightOpen() {
return rightOpen;
}
public boolean isLeftOpen() {
return leftOpen;
}
public boolean isRightClosed() {
return ! isRightOpen();
}
public boolean isLeftClosed() {
return ! isLeftOpen();
}
public Processor<IN,OUT> buildProcessor() {
List<Object> runtimeComponents = instantiateComponents();
Object first = runtimeComponents.get(0);
Object last = runtimeComponents.get(runtimeComponents.size()-1);//can be the same
illegalstate.assertTrue(isLeftOpen() && isRightOpen());
Publisher<OUT> end = (Publisher<OUT>)last;
Subscriber<IN> start = (Subscriber<IN>)first;
return new Processor<IN,OUT>() {
@Override
public void onSubscribe(Subscription s) {
start.onSubscribe(s);
}
@Override
public void onNext(IN t) {
start.onNext(t);
}
@Override
public void onError(Throwable t) {
start.onError(t);
}
@Override
public void onComplete() {
start.onComplete();
}
@Override
public void subscribe(Subscriber<? super OUT> s) {
end.subscribe(s);
}
};
}
public <T> LeftClosed<T,OUT> buildLeftClosed(Class<T> cl) {
List<Object> runtimeComponents = instantiateComponents();
Object first = runtimeComponents.get(0);
Object last = runtimeComponents.get(runtimeComponents.size()-1);//can be the same
Publisher<OUT> end = (Publisher<OUT>)last;
Publisher<?> start = (Publisher<?>)first;
return new LeftClosed<T, OUT>() {
@Override
public T get() {
return (T)start;
}
@Override
public void subscribe(Subscriber<? super OUT> s) {
end.subscribe(s);
}
};
}
public <T> RightClosed<IN,T> buildRightClosed(Class<T> cl) {
List<Object> runtimeComponents = instantiateComponents();
Object first = runtimeComponents.get(0);
Object last = runtimeComponents.get(runtimeComponents.size()-1);//can be the same
Subscriber<IN> start = (Subscriber<IN>)first;
return new RightClosed<IN, T>() {
@Override
public T get() {
return (T)last;
}
@Override
public void onSubscribe(Subscription s) {
start.onSubscribe(s);
}
@Override
public void onNext(IN t) {
start.onNext(t);
}
@Override
public void onError(Throwable t) {
start.onError(t);
}
@Override
public void onComplete() {
start.onComplete();
}
};
}
public <X,Y> ClosedPipe<X,Y> buildClosed(Class<X> clx, Class<Y> cly) {
List<Object> runtimeComponents = instantiateComponents();
Object first = runtimeComponents.get(0);
Object last = runtimeComponents.get(runtimeComponents.size()-1);//can be the same
return new ClosedPipe<X, Y>() {
@Override
public X head() {
return (X)first;
}
@Override
public Y tail() {
return (Y)last;
}
};
}
private List<Object> instantiateComponents() {
LinkedList<Object> res = new LinkedList<>();
ListIterator<Object> itr = components.listIterator();
Object prev = instantiateComponent(itr.next());
res.add(prev);
while(itr.hasNext()) {
Object next = instantiateComponent(itr.next());
link(prev, next);
res.add(next);
prev = next;
}
return res;
}
//@SuppressWarnings("unchecked")
private void link(Object prev, Object next) {
((Publisher<?>)prev).subscribe((Subscriber<? super Object>)next);
}
private Object instantiateComponent(Object c) {
return c instanceof Factory ? ((Factory<?>)c).get() : c;
}
}