/*
# Licensed Materials - Property of IBM
# Copyright IBM Corp. 2015
*/
package com.ibm.streamsx.topology.builder;
import static com.ibm.streamsx.topology.generator.operator.OpProperties.LANGUAGE;
import static com.ibm.streamsx.topology.generator.operator.OpProperties.LANGUAGE_JAVA;
import static com.ibm.streamsx.topology.generator.operator.OpProperties.MODEL;
import static com.ibm.streamsx.topology.generator.operator.OpProperties.MODEL_SPL;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.ibm.json.java.JSONArray;
import com.ibm.json.java.JSONObject;
import com.ibm.streams.flow.declare.InputPortDeclaration;
import com.ibm.streams.flow.declare.OperatorInvocation;
import com.ibm.streams.flow.declare.OutputPortDeclaration;
import com.ibm.streams.operator.Attribute;
import com.ibm.streams.operator.Operator;
import com.ibm.streams.operator.StreamSchema;
import com.ibm.streams.operator.Type.MetaType;
import com.ibm.streams.operator.model.Namespace;
import com.ibm.streams.operator.model.PrimitiveOperator;
import com.ibm.streamsx.topology.function.Supplier;
import com.ibm.streamsx.topology.tuple.JSONAble;
// Union(A,B)
// OpC(A,B)
// Union(A,B) -->
// Parallel
/**
* JSON representation.
*
* Operator:
*
* params: Object of Parameters, keyed by parameter name. outputs: Array of
* output ports (see BStream) inputs: Array in input ports type: connections:
* array of connection names as strings
*
* Parameter value: Value of parameter.
*/
public class BOperatorInvocation extends BOperator {
private final OperatorInvocation<? extends Operator> op;
protected List<BInputPort> inputs;
protected List<BOutputPort> outputs;
private final JSONObject jparams = new JSONObject();
public BOperatorInvocation(GraphBuilder bt,
Class<? extends Operator> opClass,
Map<String, ? extends Object> params) {
super(bt);
op = bt.graph().addOperator(opClass);
json().put("name", op.getName());
json().put("parameters", jparams);
if (!Operator.class.equals(opClass)) {
json().put(MODEL, MODEL_SPL);
json().put(LANGUAGE, LANGUAGE_JAVA);
json().put("kind", getKind(opClass));
json().put("kind.javaclass", opClass.getCanonicalName());
}
if (params != null) {
for (String paramName : params.keySet()) {
setParameter(paramName, params.get(paramName));
}
}
}
public BOperatorInvocation(GraphBuilder bt,
String name,
Class<? extends Operator> opClass,
Map<String, ? extends Object> params) {
super(bt);
op = bt.graph().addOperator(name, opClass);
json().put("name", op.getName());
json().put("parameters", jparams);
if (!Operator.class.equals(opClass)) {
json().put(MODEL, MODEL_SPL);
json().put(LANGUAGE, LANGUAGE_JAVA);
json().put("kind", getKind(opClass));
json().put("kind.javaclass", opClass.getCanonicalName());
}
if (params != null) {
for (String paramName : params.keySet()) {
setParameter(paramName, params.get(paramName));
}
}
}
public BOperatorInvocation(GraphBuilder bt,
Map<String, ? extends Object> params) {
this(bt, Operator.class, params);
}
public BOperatorInvocation(GraphBuilder bt,
String name,
Map<String, ? extends Object> params) {
this(bt, name, Operator.class, params);
}
/*
public BOperatorInvocation(GraphBuilder bt, String kind,
Map<String, ? extends Object> params) {
this(bt, Operator.class, params);
json().put("kind", kind);
}
*/
public void setParameter(String name, Object value) {
Object jsonValue = value;
String jsonType = null;
// Set the value in the OperatorInvocation.
if (value instanceof JSONAble) {
value = ((JSONAble)value).toJSON();
}
if (value instanceof JSONObject) {
JSONObject jo = ((JSONObject) value);
if (jo.get("type") == null || jo.get("value") == null)
throw new IllegalArgumentException("Illegal JSONObject " + jo);
String type = (String) jo.get("type");
Object val = (JSONObject) jo.get("value");
if ("__spl_value".equals(type)) {
/*
* The Value object is
* <pre><code>
* object {
* type : "__spl_value"
* value : object {
* value : any. non-null. type appropriate for metaType
* metaType : com.ibm.streams.operator.Type.MetaType.name() string
* }
* }
* </code></pre>
*/
// unwrap and fall through to handling for the wrapped value
JSONObject splValue = (JSONObject) val;
value = splValue.get("value");
jsonValue = value;
String metaType = (String) splValue.get("metaType");
if ("USTRING".equals(metaType)
|| "UINT8".equals(metaType)
|| "UINT16".equals(metaType)
|| "UINT32".equals(metaType)
|| "UINT64".equals(metaType)) {
jsonType = metaType;
}
// fall through to handle jsonValue as usual
}
else {
// other kinds of JSONObject handled below
}
}
else if (value instanceof Supplier<?>) {
value = ((Supplier<?>) value).get();
jsonValue = value;
}
if (value instanceof String) {
op.setStringParameter(name, (String) value);
if (jsonType == null)
jsonType = MetaType.RSTRING.name();
} else if (value instanceof Byte) {
op.setByteParameter(name, (Byte) value);
if (jsonType == null)
jsonType = MetaType.INT8.name();
} else if (value instanceof Short) {
op.setShortParameter(name, (Short) value);
if (jsonType == null)
jsonType = MetaType.INT16.name();
} else if (value instanceof Integer) {
op.setIntParameter(name, (Integer) value);
if (jsonType == null)
jsonType = MetaType.INT32.name();
} else if (value instanceof Long) {
op.setLongParameter(name, (Long) value);
if (jsonType == null)
jsonType = MetaType.INT64.name();
} else if (value instanceof Float) {
op.setFloatParameter(name, (Float) value);
jsonType = MetaType.FLOAT32.name();
} else if (value instanceof Double) {
op.setDoubleParameter(name, (Double) value);
jsonType = MetaType.FLOAT64.name();
} else if (value instanceof Boolean) {
op.setBooleanParameter(name, (Boolean) value);
} else if (value instanceof BigDecimal) {
op.setBigDecimalParameter(name, (BigDecimal) value);
jsonValue = value.toString(); // Need to maintain exact value
} else if (value instanceof Enum) {
op.setCustomLiteralParameter(name, (Enum<?>) value);
jsonValue = ((Enum<?>) value).name();
jsonType = JParamTypes.TYPE_ENUM;
} else if (value instanceof StreamSchema) {
jsonValue = ((StreamSchema) value).getLanguageType();
jsonType = JParamTypes.TYPE_SPLTYPE;
} else if (value instanceof String[]) {
String[] sa = (String[]) value;
JSONArray a = new JSONArray(sa.length);
for (String vs : sa)
a.add(vs);
jsonValue = a;
op.setStringParameter(name, sa);
} else if (value instanceof Attribute) {
Attribute attr = (Attribute) value;
jsonValue = attr.getName();
jsonType = JParamTypes.TYPE_ATTRIBUTE;
op.setAttributeParameter(name, attr.getName());
} else if (value instanceof JSONObject) {
JSONObject jo = (JSONObject) value;
jsonType = (String) jo.get("type");
jsonValue = (JSONObject) jo.get("value");
} else {
throw new IllegalArgumentException("Type for parameter " + name + " is not supported:" + value.getClass());
}
// Set the value as JSON
JSONObject param = new JSONObject();
param.put("value", jsonValue);
if (jsonType != null)
param.put("type", jsonType);
jparams.put(name, param);
}
public BOutputPort addOutput(StreamSchema schema) {
if (outputs == null)
outputs = new ArrayList<>();
final OutputPortDeclaration port = op.addOutput(schema);
final BOutputPort stream = new BOutputPort(this, port);
outputs.add(stream);
return stream;
}
public BInputPort inputFrom(BOutput output, BInputPort input) {
if (input != null) {
assert input.operator() == this.op;
assert inputs != null;
output.connectTo(input);
return input;
}
if (inputs == null) {
inputs = new ArrayList<>();
}
InputPortDeclaration inputPort = op.addInput(this.op.getName() + "_IN" + inputs.size(),
output.schema());
input = new BInputPort(this, inputPort);
inputs.add(input);
output.connectTo(input);
return input;
}
@Override
public JSONObject complete() {
final JSONObject json = super.complete();
if (outputs != null) {
JSONArray oa = new JSONArray(outputs.size());
for (BOutputPort output : outputs) {
oa.add(output.complete());
}
json.put("outputs", oa);
}
if (inputs != null) {
JSONArray ia = new JSONArray(inputs.size());
for (BInputPort input : inputs) {
ia.add(input.complete());
}
json.put("inputs", ia);
}
return json;
}
// Needed by the DependencyResolver to determine whether the operator
// has a 'jar' parameter by calling
//
// op instance of FunctionFunctor
public OperatorInvocation<? extends Operator> op() {
return op;
}
private static String getKind(Class<?> opClass) {
PrimitiveOperator primitive = opClass.getAnnotation(PrimitiveOperator.class);
final String kindName = primitive.name().length() == 0 ?
opClass.getSimpleName() : primitive.name();
String namespace;
if (primitive.namespace().length() != 0)
namespace = primitive.namespace();
else {
Package pkg = opClass.getPackage();
if (pkg != null) {
Namespace ns = pkg.getAnnotation(Namespace.class);
if (ns == null)
namespace = pkg.getName();
else
namespace = ns.value();
}
else {
namespace = "";
}
}
return namespace + "::" + kindName;
}
}