/*
# Licensed Materials - Property of IBM
# Copyright IBM Corp. 2015
*/
package com.ibm.streamsx.topology.internal.core;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.ibm.streams.operator.window.StreamWindow;
import com.ibm.streams.operator.window.StreamWindow.Policy;
import com.ibm.streams.operator.window.StreamWindow.Type;
import com.ibm.streamsx.topology.TStream;
import com.ibm.streamsx.topology.TWindow;
import com.ibm.streamsx.topology.builder.BInputPort;
import com.ibm.streamsx.topology.builder.BOperatorInvocation;
import com.ibm.streamsx.topology.function.BiFunction;
import com.ibm.streamsx.topology.function.Function;
import com.ibm.streamsx.topology.internal.functional.ObjectUtils;
import com.ibm.streamsx.topology.internal.functional.ops.FunctionAggregate;
import com.ibm.streamsx.topology.internal.functional.ops.FunctionJoin;
import com.ibm.streamsx.topology.internal.functional.ops.FunctionWindow;
import com.ibm.streamsx.topology.internal.logic.LogicUtils;
import com.ibm.streamsx.topology.logic.Identity;
public class WindowDefinition<T,K> extends TopologyItem implements TWindow<T,K> {
private final TStream<T> stream;
// This is the eviction policy in SPL terms
protected final StreamWindow.Policy policy;
protected final long config;
protected final TimeUnit timeUnit;
private final Function<? super T,? extends K> keyGetter;
private WindowDefinition(TStream<T> stream, StreamWindow.Policy policy, long config, TimeUnit timeUnit, Function<? super T,? extends K> keyGetter) {
super(stream);
this.stream = stream;
this.policy = policy;
this.config = config;
this.keyGetter = keyGetter;
this.timeUnit = timeUnit;
assert (timeUnit == null && policy != StreamWindow.Policy.TIME) ||
(timeUnit != null && policy == StreamWindow.Policy.TIME);
}
public WindowDefinition(TStream<T> stream, int count) {
this(stream, Policy.COUNT, count, null, null);
}
public WindowDefinition(TStream<T> stream, long time, TimeUnit unit) {
this(stream, Policy.TIME, time, unit, null);
}
public WindowDefinition(TStream<T> stream, TWindow<?,?> configWindow) {
this(stream, ((WindowDefinition<?,?>) configWindow).policy,
((WindowDefinition<?,?>) configWindow).config,
((WindowDefinition<?,?>) configWindow).timeUnit,
null);
}
private final void setPartitioned(final java.lang.reflect.Type type) {
if (type instanceof Class) {
topology().addClassDependency((Class<?>) type);
return;
}
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
java.lang.reflect.Type rawType = pt.getRawType();
if (rawType instanceof Class) {
topology().addClassDependency((Class<?>) rawType);
return;
}
}
}
@Override
public boolean isKeyed() {
return keyGetter != null;
}
@Override
public TStream<T> getStream() {
return stream;
}
@Override
public Class<T> getTupleClass() {
return stream.getTupleClass();
}
@Override
public java.lang.reflect.Type getTupleType() {
return stream.getTupleType();
}
@Override
public <A> TStream<A> aggregate(Function<List<T>, A> aggregator) {
java.lang.reflect.Type aggregateType = TypeDiscoverer.determineStreamType(aggregator, null);
return aggregate(aggregator, aggregateType, Policy.COUNT, 1, null);
}
@Override
public <A> TStream<A> aggregate(Function<List<T>, A> aggregator,
long period, TimeUnit unit) {
if (period == 0)
throw new IllegalArgumentException("Aggregate period cannot be zero.");
java.lang.reflect.Type aggregateType = TypeDiscoverer.determineStreamType(aggregator, null);
return aggregate(aggregator, aggregateType, Policy.TIME, period, unit);
}
private <A> TStream<A> aggregate(Function<List<T>, A> aggregator,
java.lang.reflect.Type aggregateType, Policy triggerPolicy, Object triggerConfig, TimeUnit triggerTimeUnit) {
if (getTupleClass() == null && !isKeyed()) {
java.lang.reflect.Type tupleType = TypeDiscoverer.determineStreamTypeNested(Function.class, 0, List.class, aggregator);
setPartitioned(tupleType);
}
String opName = LogicUtils.functionName(aggregator);
if (opName.isEmpty()) {
opName = TypeDiscoverer.getTupleName(getTupleType()) + "Aggregate";
}
BOperatorInvocation aggOp = JavaFunctional.addFunctionalOperator(this,
opName, FunctionAggregate.class, aggregator, getOperatorParams());
SourceInfo.setSourceInfo(aggOp, WindowDefinition.class);
addInput(aggOp, triggerPolicy, triggerConfig, triggerTimeUnit);
return JavaFunctional.addJavaOutput(this, aggOp, aggregateType);
}
private Map<String,Object> getOperatorParams() {
Map<String,Object> params = new HashMap<>();
if (isKeyed())
params.put(FunctionWindow.WINDOW_KEY_GETTER_PARAM, ObjectUtils.serializeLogic(keyGetter));
return params;
}
public BInputPort addInput(BOperatorInvocation aggOp,
StreamWindow.Policy triggerPolicy, Object triggerConfig, TimeUnit triggerTimeUnit) {
BInputPort bi = stream.connectTo(aggOp, true, null);
return bi.window(Type.SLIDING, policy, config, timeUnit,
triggerPolicy, triggerConfig, triggerTimeUnit, isKeyed());
}
public <J, U> TStream<J> joinInternal(TStream<U> xstream,
Function<? super U, ? extends K> xstreamKey,
BiFunction<U, List<T>, J> joiner, java.lang.reflect.Type tupleType) {
String opName = LogicUtils.functionName(joiner);
if (opName.isEmpty()) {
opName = TypeDiscoverer.getTupleName(getTupleType()) + "Join";
}
Map<String, Object> params = getOperatorParams();
if (isKeyed() && xstreamKey != null) {
params.put(FunctionJoin.JOIN_KEY_GETTER_PARAM, ObjectUtils.serializeLogic(xstreamKey));
}
BOperatorInvocation joinOp = JavaFunctional.addFunctionalOperator(this,
opName, FunctionJoin.class, joiner, params);
SourceInfo.setSourceInfo(joinOp, WindowDefinition.class);
@SuppressWarnings("unused")
BInputPort input0 = addInput(joinOp, Policy.COUNT, Integer.MAX_VALUE, (TimeUnit) null);
@SuppressWarnings("unused")
BInputPort input1 = xstream.connectTo(joinOp, true, null);
return JavaFunctional.addJavaOutput(this, joinOp, tupleType);
}
@Override
public <U> TWindow<T,U> key(Function<? super T, ? extends U> keyGetter) {
if (keyGetter == null)
throw new NullPointerException();
return new WindowDefinition<T,U>(stream, policy, config, timeUnit, keyGetter);
}
@Override
public TWindow<T, T> key() {
return key(new Identity<T>());
}
}