/** * 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.graph; import java.lang.reflect.Type; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.asakusafw.vocabulary.flow.FlowDescription; import com.asakusafw.vocabulary.flow.Source; /** * A description of flow-part. * @since 0.1.0 * @version 0.9.1 */ public class FlowPartDescription implements FlowElementDescription { private static final Map<Class<? extends FlowElementAttribute>, FlowElementAttribute> ATTRIBUTES; static { Map<Class<? extends FlowElementAttribute>, FlowElementAttribute> map = new HashMap<>(); map.put(FlowBoundary.class, FlowBoundary.STAGE); ATTRIBUTES = Collections.unmodifiableMap(map); } private final FlowGraph flowGraph; private final List<FlowElementPortDescription> inputPorts; private final List<FlowElementPortDescription> outputPorts; private final List<Parameter> parameters; private String name; /** * Creates a new instance. * @param flowGraph the flow-graph which represents this flow-part structure * @throws IllegalArgumentException if the parameter is {@code null} */ public FlowPartDescription(FlowGraph flowGraph) { this(flowGraph, Collections.emptyList()); } /** * Creates a new instance. * @param flowGraph the flow-graph which represents this flow-part structure * @param parameters the parameters for this flow-part * @throws IllegalArgumentException if some parameters were {@code null} * @since 0.5.0 */ public FlowPartDescription(FlowGraph flowGraph, List<? extends Parameter> parameters) { if (flowGraph == null) { throw new IllegalArgumentException("flowGraph must not be null"); //$NON-NLS-1$ } if (parameters == null) { throw new IllegalArgumentException("parameters must not be null"); //$NON-NLS-1$ } this.flowGraph = flowGraph; List<FlowElementPortDescription> inputs = new ArrayList<>(); List<FlowElementPortDescription> outputs = new ArrayList<>(); this.inputPorts = Collections.unmodifiableList(inputs); this.outputPorts = Collections.unmodifiableList(outputs); this.parameters = Collections.unmodifiableList(new ArrayList<>(parameters)); for (FlowIn<?> in : flowGraph.getFlowInputs()) { inputs.add(new FlowElementPortDescription( in.getDescription().getName(), in.getDescription().getDataType(), PortDirection.INPUT)); } for (FlowOut<?> out : flowGraph.getFlowOutputs()) { outputs.add(new FlowElementPortDescription( out.getDescription().getName(), out.getDescription().getDataType(), PortDirection.OUTPUT)); } } /** * Returns the flow-graph which represents this flow-part structure. * @return the flow-graph */ public FlowGraph getFlowGraph() { return flowGraph; } /** * Returns the parameters for this flow-part. * @return the parameters * @since 0.5.0 */ public List<Parameter> getParameters() { return parameters; } @Override public FlowElementKind getKind() { return FlowElementKind.FLOW_COMPONENT; } @Override public FlowElementDescription getOrigin() { return this; } @Override public String getName() { if (name == null) { return flowGraph.getDescription().getSimpleName(); } return name; } @Override public void setName(String newName) { if (newName == null) { throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$ } this.name = newName; } @Override public List<FlowElementPortDescription> getInputPorts() { return inputPorts; } @Override public List<FlowElementPortDescription> getOutputPorts() { return outputPorts; } /** * Returns the flow-inside input ports that individually corresponded to each flow-outside input port. * Note that each flow-inside port is a member of {@link #getFlowGraph() flow graph}, on the other hand, * each flow-outside port is a member of {@link #getInputPorts() input ports} of this element. * @param externalInput flow-outside port * @return the corresponded flow-inside port * @throws IllegalArgumentException if the parameter is {@code null} */ public FlowIn<?> getInternalInputPort(FlowElementPortDescription externalInput) { if (externalInput == null) { throw new IllegalArgumentException("externalInput must not be null"); //$NON-NLS-1$ } assert inputPorts.size() == flowGraph.getFlowInputs().size(); int index = inputPorts.indexOf(externalInput); if (index < 0) { throw new IllegalArgumentException(); } return flowGraph.getFlowInputs().get(index); } /** * Returns the flow-inside outputs ports that individually corresponded to each flow-outside output port. * Note that each flow-inside port is a member of {@link #getFlowGraph() flow graph}, on the other hand, * each flow-outside port is a member of {@link #getOutputPorts() output ports} of this element. * @param externalOutput flow-outside port * @return the corresponded flow-inside port * @throws IllegalArgumentException if the parameter is {@code null} */ public FlowOut<?> getInternalOutputPort(FlowElementPortDescription externalOutput) { if (externalOutput == null) { throw new IllegalArgumentException("externalOutput must not be null"); //$NON-NLS-1$ } assert outputPorts.size() == flowGraph.getFlowOutputs().size(); int index = outputPorts.indexOf(externalOutput); if (index < 0) { throw new IllegalArgumentException(); } return flowGraph.getFlowOutputs().get(index); } @Override public List<FlowResourceDescription> getResources() { return Collections.emptyList(); } @Override public Set<? extends Class<? extends FlowElementAttribute>> getAttributeTypes() { return ATTRIBUTES.keySet(); } @Override public <T extends FlowElementAttribute> T getAttribute(Class<T> attributeClass) { if (attributeClass == null) { throw new IllegalArgumentException("attributeClass must not be null"); //$NON-NLS-1$ } Object attribute = ATTRIBUTES.get(attributeClass); return attributeClass.cast(attribute); } @Override public String toString() { return getName(); } /** * Represents a parameter and its argument for flow-part. * @since 0.5.0 */ public static class Parameter { private final String name; private final Type type; private final Object value; /** * Creates a new instance. * @param name the parameter name * @param type the parameter type * @param value the argument value (nullable) * @throws IllegalArgumentException if some parameters were {@code null} */ public Parameter(String name, Type type, Object value) { if (name == null) { throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$ } if (type == null) { throw new IllegalArgumentException("type must not be null"); //$NON-NLS-1$ } this.name = name; this.type = type; this.value = value; } /** * Returns the parameter name. * @return the parameter name */ public String getName() { return name; } /** * Returns the parameter type. * @return the parameter type */ public Type getType() { return type; } /** * Returns the argument value. * @return the argument value (nullable) */ public Object getValue() { return value; } @Override public String toString() { return MessageFormat.format( "{0}[{1}]={2}", //$NON-NLS-1$ getName(), getType(), getValue()); } } /** * A builder for building {@link FlowPartDescription}. * @since 0.1.0 * @version 0.5.0 */ public static class Builder { private final Class<? extends FlowDescription> declaring; private final List<FlowIn<?>> flowInputs; private final List<FlowOut<?>> flowOutputs; private final List<Parameter> parameters; /** * Creates a new instance. * @param declaring the class declaring the target flow-part * @throws IllegalArgumentException if the parameter is {@code null} */ public Builder(Class<? extends FlowDescription> declaring) { if (declaring == null) { throw new IllegalArgumentException("declaring must not be null"); //$NON-NLS-1$ } this.declaring = declaring; this.flowInputs = new ArrayList<>(); this.flowOutputs = new ArrayList<>(); this.parameters = new ArrayList<>(); } /** * Add a new input port into the target flow-part. * @param <T> the data type * @param name the port name * @param type the data type * @return the {@link FlowIn} object that represents the defined port * @throws IllegalArgumentException if some parameters are {@code null} */ public <T> FlowIn<T> addInput(String name, Type type) { if (name == null) { throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$ } if (type == null) { throw new IllegalArgumentException("type must not be null"); //$NON-NLS-1$ } FlowIn<T> in = new FlowIn<>(new InputDescription(name, type)); flowInputs.add(in); return in; } /** * Add a new output port into the target flow-part. * @param <T> the data type * @param name the port name * @param type the data type * @return the {@link FlowOut} object that represents the defined port * @throws IllegalArgumentException if some parameters are {@code null} */ public <T> FlowOut<T> addOutput(String name, Type type) { if (name == null) { throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$ } if (type == null) { throw new IllegalArgumentException("type must not be null"); //$NON-NLS-1$ } FlowOut<T> out = new FlowOut<>(new OutputDescription(name, type)); flowOutputs.add(out); return out; } /** * Add a new input port into the target flow-part. * @param <T> the data type * @param name the port name * @param typeReference a source that has the data type as same to the creating port * @return the {@link FlowIn} object that represents the defined port * @throws IllegalArgumentException if some parameters are {@code null} */ public <T> FlowIn<T> addInput(String name, Source<T> typeReference) { if (name == null) { throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$ } if (typeReference == null) { throw new IllegalArgumentException("type must not be null"); //$NON-NLS-1$ } Type type = typeReference.toOutputPort().getDescription().getDataType(); return addInput(name, type); } /** * Add a new output port into the target flow-part. * @param <T> the data type * @param name the port name * @param typeReference a source that has the data type as same to the creating port * @return the {@link FlowOut} object that represents the defined port * @throws IllegalArgumentException if some parameters are {@code null} */ public <T> FlowOut<T> addOutput(String name, Source<T> typeReference) { if (name == null) { throw new IllegalArgumentException("name must not be null"); //$NON-NLS-1$ } if (typeReference == null) { throw new IllegalArgumentException("typeReference must not be null"); //$NON-NLS-1$ } Type type = typeReference.toOutputPort().getDescription().getDataType(); return addOutput(name, type); } /** * Adds a parameter and its argument value for this flow-part. * @param parameterName the parameter name * @param parameterType the parameter value * @param argument the actual parameter argument, or {@code null} if the argument is just {@code null} * @throws IllegalArgumentException if some parameters were {@code null} * @since 0.5.0 */ public void addParameter(String parameterName, Type parameterType, Object argument) { if (parameterName == null) { throw new IllegalArgumentException("parameterName must not be null"); //$NON-NLS-1$ } if (parameterType == null) { throw new IllegalArgumentException("parameterType must not be null"); //$NON-NLS-1$ } parameters.add(new Parameter(parameterName, parameterType, argument)); } /** * Creates a new description object from the previously information. * @return the created description */ public FlowPartDescription toDescription() { FlowGraph graph = new FlowGraph(declaring, flowInputs, flowOutputs); return new FlowPartDescription(graph); } /** * Creates a new {@link FlowElementResolver} object from the previously information. * @param desc the original flow description object * @return the created object */ public FlowElementResolver toResolver(FlowDescription desc) { if (desc == null) { throw new IllegalArgumentException("desc must not be null"); //$NON-NLS-1$ } desc.start(); return new FlowElementResolver(toDescription()); } } }