/**
* 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.compiler.flow;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.asakusafw.compiler.flow.plan.FlowPath;
import com.asakusafw.vocabulary.flow.FlowDescription;
import com.asakusafw.vocabulary.flow.graph.FlowBoundary;
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.FlowElementPortDescription;
import com.asakusafw.vocabulary.flow.graph.FlowGraph;
import com.asakusafw.vocabulary.flow.graph.FlowIn;
import com.asakusafw.vocabulary.flow.graph.FlowOut;
import com.asakusafw.vocabulary.flow.graph.FlowPartDescription;
import com.asakusafw.vocabulary.flow.graph.InputDescription;
import com.asakusafw.vocabulary.flow.graph.OperatorDescription;
import com.asakusafw.vocabulary.flow.graph.OutputDescription;
import com.asakusafw.vocabulary.flow.graph.PortConnection;
import com.asakusafw.vocabulary.flow.graph.PortDirection;
import com.asakusafw.vocabulary.flow.util.PseudElementDescription;
import com.asakusafw.vocabulary.operator.Identity;
/**
* Flow graph generator for testing.
*/
public class FlowGraphGenerator {
private static final Class<String> TYPE = String.class;
private final List<FlowIn<?>> flowInputs = new ArrayList<>();
private final List<FlowOut<?>> flowOutputs = new ArrayList<>();
private final Map<String, FlowElement> elements = new HashMap<>();
/**
* Adds an input.
* @param name the element name
* @return the added element
*/
public FlowElement defineInput(String name) {
InputDescription desc = new InputDescription(name, TYPE);
FlowIn<?> node = new FlowIn<>(desc);
flowInputs.add(node);
return register(name, node.getFlowElement());
}
/**
* Adds an output.
* @param name the element name
* @return the added element
*/
public FlowElement defineOutput(String name) {
OutputDescription desc = new OutputDescription(name, TYPE);
FlowOut<?> node = new FlowOut<>(desc);
flowOutputs.add(node);
return register(name, node.getFlowElement());
}
/**
* Adds an operator.
* @param name the element name
* @param inputList the space separated input port names
* @param outputList the space separated output port names
* @param attributes the attributes
* @return the added element
*/
public FlowElement defineOperator(
String name,
String inputList,
String outputList,
FlowElementAttribute... attributes) {
Class<String> type = TYPE;
return defineOperator(type, name, inputList, outputList, attributes);
}
/**
* Defines a new operator and registers into this generator.
* @param type target operator type
* @param name target name
* @param inputList target input names
* @param outputList target output names
* @param attributes operator attributes
* @return the defined element
*/
public FlowElement defineOperator(
Class<?> type, String name,
String inputList, String outputList,
FlowElementAttribute... attributes) {
List<FlowElementPortDescription> inputs = parsePorts(PortDirection.INPUT, inputList);
List<FlowElementPortDescription> outputs = parsePorts(PortDirection.OUTPUT, outputList);
FlowElementDescription desc = new OperatorDescription(
new OperatorDescription.Declaration(
Identity.class,
type,
type,
name,
Collections.emptyList()),
inputs,
outputs,
Collections.emptyList(),
Collections.emptyList(),
Arrays.asList(attributes));
return register(name, desc);
}
/**
* Adds an flow-part.
* @param name the element name
* @param graph the flow graph
* @return the added element
*/
public FlowElement defineFlowPart(
String name,
FlowGraph graph) {
FlowElementDescription desc = new FlowPartDescription(graph);
return register(name, desc);
}
/**
* Adds an empty operator.
* @param name the element name
* @return the added element
*/
public FlowElement defineEmpty(String name) {
return register(name, new PseudElementDescription(
name,
TYPE,
false,
true,
FlowBoundary.STAGE));
}
/**
* Adds .
* @param name the element name
* @return the added element
*/
public FlowElement defineStop(String name) {
return register(name, new PseudElementDescription(
name,
TYPE,
true,
false,
FlowBoundary.STAGE));
}
/**
* Adds a pseudo-element.
* @param name the element name
* @param attributes the attributes
* @return the added element
*/
public FlowElement definePseud(
String name,
FlowElementAttribute... attributes) {
return register(name, new PseudElementDescription(
name,
TYPE,
true,
true,
attributes));
}
/**
* Connects ports.
* The port should be described as {@code "element-name.port-name"}, or
* {@code "element-name"} for single port elements.
* @param upstream the upstream
* @param downstream the downstream
* @return this
*/
public FlowGraphGenerator connect(String upstream, String downstream) {
FlowElementOutput output = findOutput(upstream);
FlowElementInput input = findInput(downstream);
PortConnection.connect(output, input);
return this;
}
/**
* Returns an element.
* @param name the target name
* @return the target element
*/
public FlowElement get(String name) {
FlowElement found = elements.get(name);
if (found == null) {
throw new AssertionError(name + elements.keySet());
}
return found;
}
/**
* Returns an element description.
* @param name the target name
* @return the target element
*/
public FlowElementDescription desc(String name) {
FlowElement found = elements.get(name);
if (found == null) {
throw new AssertionError(name + elements.keySet());
}
return found.getDescription();
}
/**
* Returns an input.
* @param input the input name
* @return the target port
*/
public FlowElementInput input(String input) {
return findInput(input);
}
/**
* Returns inputs.
* @param inputs the input names
* @return the target ports
*/
public Set<FlowElementInput> inputs(String... inputs) {
Set<FlowElementInput> results = new HashSet<>();
for (String input : inputs) {
results.add(input(input));
}
return results;
}
/**
* Returns an output.
* @param output the output name
* @return the target port
*/
public FlowElementOutput output(String output) {
return findOutput(output);
}
/**
* Returns outputs.
* @param outputs the output names
* @return the target ports
*/
public Set<FlowElementOutput> outputs(String... outputs) {
Set<FlowElementOutput> results = new HashSet<>();
for (String output : outputs) {
results.add(output(output));
}
return results;
}
/**
* Returns the set of elements.
* @param names the element names
* @return the elements
*/
public Set<FlowElement> getAsSet(String... names) {
Set<FlowElement> results = new HashSet<>();
for (String name : names) {
results.add(get(name));
}
return results;
}
/**
* Returns the all elements.
* @return the all elements
*/
public Set<FlowElement> all() {
return new HashSet<>(elements.values());
}
/**
* Returns the flow graph.
* @return the flow graph
*/
public FlowGraph toGraph() {
return new FlowGraph(Testing.class, flowInputs, flowOutputs);
}
/**
* Returns the flow path.
* @param direction the path direction
* @return the flow path
*/
public FlowPath toPath(FlowPath.Direction direction) {
Set<FlowElement> inputs = new HashSet<>();
Set<FlowElement> passings = new HashSet<>();
Set<FlowElement> outputs = new HashSet<>();
for (FlowIn<?> node : flowInputs) {
inputs.add(node.getFlowElement());
}
for (FlowOut<?> node : flowOutputs) {
outputs.add(node.getFlowElement());
}
passings.removeAll(inputs);
passings.removeAll(outputs);
return new FlowPath(
direction,
direction == FlowPath.Direction.FORWARD ? inputs : outputs,
passings,
direction == FlowPath.Direction.FORWARD ? outputs : inputs);
}
private static final Pattern PORT = Pattern.compile("(.+?)(\\.(.+?))?");
private FlowElementInput findInput(String spec) {
Matcher matcher = PORT.matcher(spec);
if (matcher.matches() == false) {
throw new AssertionError(spec);
}
String elementName = matcher.group(1);
FlowElement element = elements.get(elementName);
if (element == null) {
throw new AssertionError(elementName + elements.keySet());
}
String portName = matcher.group(3);
if (portName == null) {
if (element.getInputPorts().size() != 1) {
throw new AssertionError(element.getInputPorts());
}
return element.getInputPorts().get(0);
}
FlowElementInput port = null;
for (FlowElementInput finding : element.getInputPorts()) {
if (portName.equals(finding.getDescription().getName())) {
port = finding;
break;
}
}
if (port == null) {
throw new AssertionError(elementName + "." + portName + elements.keySet());
}
return port;
}
private FlowElementOutput findOutput(String spec) {
Matcher matcher = PORT.matcher(spec);
if (matcher.matches() == false) {
throw new AssertionError(spec);
}
String elementName = matcher.group(1);
FlowElement element = elements.get(elementName);
if (element == null) {
throw new AssertionError(elementName + elements.keySet());
}
String portName = matcher.group(3);
if (portName == null) {
if (element.getOutputPorts().size() != 1) {
throw new AssertionError(element.getOutputPorts());
}
return element.getOutputPorts().get(0);
}
FlowElementOutput port = null;
for (FlowElementOutput finding : element.getOutputPorts()) {
if (portName.equals(finding.getDescription().getName())) {
port = finding;
break;
}
}
if (port == null) {
throw new AssertionError(elementName + "." + portName + elements.keySet());
}
return port;
}
private FlowElement register(String name, FlowElementDescription desc) {
FlowElement element = new FlowElement(desc);
return register(name, element);
}
private FlowElement register(String name, FlowElement element) {
if (elements.containsKey(name)) {
throw new AssertionError(name + elements.keySet());
}
elements.put(name, element);
return element;
}
private List<FlowElementPortDescription> parsePorts(
PortDirection direction,
String nameList) {
String[] names = nameList.trim().split("\\s+");
List<FlowElementPortDescription> results = new ArrayList<>();
for (String name : names) {
results.add(new FlowElementPortDescription(name, TYPE, direction));
}
return results;
}
private static class Testing extends FlowDescription {
@Override
protected void describe() {
return;
}
}
}