/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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.asakusafw.vocabulary.flow.builder;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.asakusafw.vocabulary.flow.FlowDescription;
import com.asakusafw.vocabulary.flow.Source;
import com.asakusafw.vocabulary.flow.graph.FlowElement;
import com.asakusafw.vocabulary.flow.graph.FlowElementAttribute;
import com.asakusafw.vocabulary.flow.graph.FlowElementDescription;
import com.asakusafw.vocabulary.flow.graph.FlowElementInput;
import com.asakusafw.vocabulary.flow.graph.FlowElementOutput;
import com.asakusafw.vocabulary.flow.graph.PortConnection;
/**
* Builds operator graphs.
* @since 0.9.0
* @version 0.9.1
*/
public abstract class FlowElementBuilder {
private static final FlowElementAttribute[] EMPTY_ATTRS = new FlowElementAttribute[0];
private final List<PortInfo> inputs = new ArrayList<>();
private final List<PortInfo> outputs = new ArrayList<>();
private final List<DataInfo> args = new ArrayList<>();
private final List<AttributeInfo> attrs = new ArrayList<>();
private final Map<String, FlowElementOutput> inputMapping = new HashMap<>();
/**
* Creates a new instance for operator method.
* @param annotationType operator annotation type.
* @param operatorClass operator class
* @param methodName operator method name
* @param methodParameterTypes the operator method parameter types
* @return created builder
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public static FlowElementBuilder createOperator(
Class<? extends Annotation> annotationType,
Class<?> operatorClass,
String methodName,
Class<?>... methodParameterTypes) {
return new OperatorNodeBuilder(
annotationType, operatorClass, operatorClass, methodName, methodParameterTypes);
}
/**
* Creates a new instance for operator method.
* @param annotationType operator annotation type.
* @param operatorClass operator class
* @param implementationClass operator implementation class
* @param methodName operator method name
* @param methodParameterTypes the operator method parameter types
* @return created builder
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public static FlowElementBuilder createOperator(
Class<? extends Annotation> annotationType,
Class<?> operatorClass,
Class<?> implementationClass,
String methodName,
Class<?>... methodParameterTypes) {
return new OperatorNodeBuilder(
annotationType, operatorClass, implementationClass, methodName, methodParameterTypes);
}
/**
* Creates a new instance for flow description class.
* @param flowDescriptionClass flow description class
* @param constructorParameterTypes constructor parameter types
* @return created builder
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public static FlowElementBuilder createFlow(
Class<? extends FlowDescription> flowDescriptionClass,
Class<?>... constructorParameterTypes) {
return new FlowNodeBuilder(flowDescriptionClass, constructorParameterTypes);
}
/**
* Defines a new input for operator.
* @param name input name
* @param upstream upstream dataset
* @return defined port information
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public PortInfo defineInput(String name, Source<?> upstream) {
return defineInput(name, upstream, EMPTY_ATTRS);
}
/**
* Defines a new input for operator.
* @param name input name
* @param upstream upstream dataset
* @param attributes the port attributes
* @return defined port information
* @throws IllegalArgumentException if some parameters were {@code null}
* @since 0.9.1
*/
public PortInfo defineInput(String name, Source<?> upstream, FlowElementAttribute... attributes) {
Objects.requireNonNull(name, "name must not be null"); //$NON-NLS-1$
Objects.requireNonNull(upstream, "upstream must not be null"); //$NON-NLS-1$
Objects.requireNonNull(attributes);
FlowElementOutput output = upstream.toOutputPort();
Type type = getType(output);
PortInfo info = defineInput0(name, type, attributes);
inputMapping.put(name, output);
return info;
}
private PortInfo defineInput0(String name, Type type, FlowElementAttribute... attributes) {
assert name != null;
assert type != null;
assert attributes != null;
PortInfo info = new PortInfo(PortInfo.Direction.INPUT, name, type, Arrays.asList(attributes));
inputs.add(info);
return info;
}
private static Type getType(FlowElementOutput output) {
return output.getDescription().getDataType();
}
/**
* Defines a new output for operator.
* @param name output name
* @param type output type
* @return defined port information
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public PortInfo defineOutput(String name, Type type) {
return defineOutput(name, type, EMPTY_ATTRS);
}
/**
* Defines a new output for operator.
* @param name output name
* @param typeRef output type reference
* @return defined port information
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public PortInfo defineOutput(String name, Source<?> typeRef) {
return defineOutput(name, typeRef, EMPTY_ATTRS);
}
/**
* Defines a new output for operator.
* @param name output name
* @param type output type
* @param attributes the port attributes
* @return defined port information
* @throws IllegalArgumentException if some parameters were {@code null}
* @since 0.9.1
*/
public PortInfo defineOutput(String name, Type type, FlowElementAttribute... attributes) {
Objects.requireNonNull(name, "name must not be null"); //$NON-NLS-1$
Objects.requireNonNull(type, "type must not be null"); //$NON-NLS-1$
Objects.requireNonNull(attributes);
return defineOutput0(name, type, attributes);
}
/**
* Defines a new output for operator.
* @param name output name
* @param typeRef output type reference
* @param attributes the port attributes
* @return defined port information
* @throws IllegalArgumentException if some parameters were {@code null}
* @since 0.9.1
*/
public PortInfo defineOutput(String name, Source<?> typeRef, FlowElementAttribute... attributes) {
Objects.requireNonNull(name, "name must not be null"); //$NON-NLS-1$
Objects.requireNonNull(typeRef, "typeRef must not be null"); //$NON-NLS-1$
Objects.requireNonNull(attributes);
return defineOutput0(name, getType(typeRef.toOutputPort()), attributes);
}
private PortInfo defineOutput0(String name, Type type, FlowElementAttribute... attributes) {
assert name != null;
assert type != null;
assert attributes != null;
PortInfo info = new PortInfo(PortInfo.Direction.OUTPUT, name, type, Arrays.asList(attributes));
outputs.add(info);
return info;
}
/**
* Defines a new data for operator.
* @param name the argument name
* @param data data representation
* @return defined data information
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public DataInfo defineData(String name, Data data) {
Objects.requireNonNull(name, "name must not be null"); //$NON-NLS-1$
Objects.requireNonNull(data, "data must not be null"); //$NON-NLS-1$
DataInfo info = new DataInfo(name, data);
args.add(info);
return info;
}
/**
* Defines a new argument for operator.
* @param name the argument name
* @param type the value type
* @param value the constant value
* @return defined data information
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public DataInfo defineData(String name, Type type, Object value) {
Objects.requireNonNull(name, "name must not be null"); //$NON-NLS-1$
return defineData(name, new Constant(type, value));
}
/**
* Defines an attribute.
* @param attribute the attribute
* @throws IllegalArgumentException if some parameters were {@code null}
*/
public void defineAttribute(Object attribute) {
Objects.requireNonNull(attribute, "attribute must not be null"); //$NON-NLS-1$
attrs.add(new AttributeInfo(attribute));
}
/**
* Resolves current operator input/output/arguments.
* @return the resolved information
* @throws IllegalStateException if failed to resolve the operator
*/
public FlowElementEditor resolve() {
FlowElement resolved = new FlowElement(build(inputs, outputs, args, attrs));
FlowElementEditor editor = new FlowElementEditor(resolved);
for (Map.Entry<String, FlowElementOutput> entry : inputMapping.entrySet()) {
FlowElementOutput upstream = entry.getValue();
FlowElementInput downstream = editor.getInput(entry.getKey());
PortConnection.connect(upstream, downstream);
}
return editor;
}
/**
* Builds a flow from operator input/output/arguments.
* @param inputPorts list of operator input
* @param outputPorts list of operator output
* @param arguments list of operator argument
* @param attributes list of operator attribute
* @return the resolved information
* @throws IllegalStateException if failed to resolve the operator
*/
protected abstract FlowElementDescription build(
List<PortInfo> inputPorts,
List<PortInfo> outputPorts,
List<DataInfo> arguments,
List<AttributeInfo> attributes);
}