Package com.asakusafw.compiler.flow.stage

Source Code of com.asakusafw.compiler.flow.stage.FragmentFlow$FragmentNode

/**
* Copyright 2011-2014 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.stage;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.TaskInputOutputContext;

import com.asakusafw.compiler.common.NameGenerator;
import com.asakusafw.compiler.common.Precondition;
import com.asakusafw.compiler.flow.DataClass;
import com.asakusafw.compiler.flow.FlowCompilingEnvironment;
import com.asakusafw.compiler.flow.FlowElementProcessor;
import com.asakusafw.compiler.flow.plan.FlowBlock;
import com.asakusafw.compiler.flow.stage.StageModel.Fragment;
import com.asakusafw.compiler.flow.stage.StageModel.ResourceFragment;
import com.asakusafw.compiler.flow.stage.StageModel.Sink;
import com.asakusafw.compiler.flow.stage.StageModel.Unit;
import com.asakusafw.runtime.core.Result;
import com.asakusafw.runtime.flow.Rendezvous;
import com.asakusafw.runtime.flow.VoidResult;
import com.asakusafw.runtime.stage.output.StageOutputDriver;
import com.asakusafw.utils.collections.Lists;
import com.asakusafw.utils.collections.Maps;
import com.asakusafw.utils.collections.Sets;
import com.asakusafw.utils.graph.Graph;
import com.asakusafw.utils.graph.Graphs;
import com.asakusafw.utils.java.model.syntax.BasicTypeKind;
import com.asakusafw.utils.java.model.syntax.Expression;
import com.asakusafw.utils.java.model.syntax.FieldDeclaration;
import com.asakusafw.utils.java.model.syntax.InfixOperator;
import com.asakusafw.utils.java.model.syntax.MethodDeclaration;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
import com.asakusafw.utils.java.model.syntax.Name;
import com.asakusafw.utils.java.model.syntax.SimpleName;
import com.asakusafw.utils.java.model.syntax.Statement;
import com.asakusafw.utils.java.model.syntax.Type;
import com.asakusafw.utils.java.model.util.AttributeBuilder;
import com.asakusafw.utils.java.model.util.ExpressionBuilder;
import com.asakusafw.utils.java.model.util.ImportBuilder;
import com.asakusafw.utils.java.model.util.Models;
import com.asakusafw.utils.java.model.util.TypeBuilder;
import com.asakusafw.vocabulary.flow.graph.FlowElement;
import com.asakusafw.vocabulary.flow.graph.FlowElementInput;
import com.asakusafw.vocabulary.flow.graph.FlowElementOutput;

/**
* 処理断片を依存関係に従って再構築する。
*/
public class FragmentFlow {

    private final FlowCompilingEnvironment environment;

    private final ImportBuilder importer;

    private final NameGenerator names;

    private final StageModel stage;

    private final List<? extends StageModel.Unit<?>> units;

    private final ShuffleModel shuffle;

    private final SimpleName stageOutputs;

    private final Map<FlowElementInput, FragmentNode> lines = Maps.create();

    private final Map<FlowElement, FragmentNode> rendezvous = Maps.create();

    private Map<ResourceFragment, SimpleName> resources = Maps.create();

    private final ModelFactory factory;

    private final Graph<FragmentNode> dependencies;

    /**
     * インスタンスを生成する。
     * @param environment 環境オブジェクト
     * @param importer インポート宣言を生成する
     * @param names 衝突しない名前を生成する
     * @param model 対象のステージを表すモデル
     * @param units 対象の処理単位群
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public FragmentFlow(
            FlowCompilingEnvironment environment,
            ImportBuilder importer,
            NameGenerator names,
            StageModel model,
            List<? extends StageModel.Unit<?>> units) {
        Precondition.checkMustNotBeNull(environment, "environment"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(importer, "importer"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(names, "names"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(model, "model"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(units, "units"); //$NON-NLS-1$
        this.environment = environment;
        this.factory = environment.getModelFactory();
        this.importer = importer;
        this.names = names;
        this.stage = model;
        this.units = units;
        this.shuffle = model.getShuffleModel();
        this.resources = createResources();
        this.dependencies = analyzeDependencies();
        resolveDependencies();
        this.stageOutputs = createStageOutputs();
    }

    private SimpleName createStageOutputs() {
        for (FragmentNode node : dependencies.getNodeSet()) {
            if (node.getKind() == Kind.OUTPUT) {
                return names.create("outputs");
            }
        }
        return null;
    }

    private Graph<FragmentNode> analyzeDependencies() {
        Map<FlowElementOutput, List<FragmentNode>> nodes = analyzeNodes();
        Graph<FragmentNode> graph = Graphs.newInstance();
        buildFragmentGraph(nodes, graph);
        buildOutputGraph(nodes, graph);
        if (shuffle != null) {
            buildShuffleGraph(nodes, graph);
        }
        return graph;
    }

    private Map<ResourceFragment, SimpleName> createResources() {
        Map<ResourceFragment, SimpleName> results = Maps.create();
        for (Unit<?> unit : units) {
            for (Fragment fragment : unit.getFragments()) {
                for (ResourceFragment resource : fragment.getResources()) {
                    if (results.containsKey(resource)) {
                        continue;
                    }
                    results.put(resource, names.create("resource"));
                }
            }
        }
        return results;
    }

    private void resolveDependencies() {
        assert dependencies != null;
        Graph<FragmentNode> tgraph = Graphs.transpose(dependencies);
        for (Graph.Vertex<FragmentNode> vertex : tgraph) {
            if (vertex.getConnected().isEmpty() == false) {
                continue;
            }
            FragmentNode node = vertex.getNode();
            collectFragment(node);
        }
    }

    private void collectFragment(FragmentNode node) throws AssertionError {
        FlowElementInput port = ((StageModel.Fragment) node.getValue()).getInputPorts().get(0);
        switch (node.getKind()) {
        case LINE:
            assert lines.containsKey(port) == false;
            lines.put(port, node);
            break;
        case RENDEZVOUS:
            assert rendezvous.containsKey(port.getOwner()) == false;
            rendezvous.put(port.getOwner(), node);
            break;
        default:
            throw new AssertionError(node.getKind());
        }
    }

    private Map<FlowElementOutput, List<FragmentNode>> analyzeNodes() {
        Set<Fragment> saw = Sets.create();
        Map<FlowElementOutput, List<FragmentNode>> results = Maps.create();
        for (StageModel.Unit<?> unit : units) {
            for (Fragment fragment : unit.getFragments()) {
                if (saw.contains(fragment)) {
                    continue;
                }
                saw.add(fragment);
                FragmentNode node;
                if (fragment.isRendezvous()) {
                    node = new FragmentNode(Kind.RENDEZVOUS, fragment, names.create("rendezvous"));
                } else {
                    node = new FragmentNode(Kind.LINE, fragment, names.create("line"));
                }
                for (FlowElementOutput output : fragment.getOutputPorts()) {
                    Maps.addToList(results, output, node);
                }
            }
        }
        return results;
    }

    private void buildFragmentGraph(
            Map<FlowElementOutput, List<FragmentNode>> nodes,
            Graph<FragmentNode> graph) {
        assert nodes != null;
        assert graph != null;
        Set<FragmentNode> saw = Sets.create();
        for (Map.Entry<FlowElementOutput, List<FragmentNode>> entry : nodes.entrySet()) {
            for (FragmentNode node : entry.getValue()) {
                if (saw.contains(node)) {
                    continue;
                }
                saw.add(node);
                graph.addNode(node);
                Fragment fragment = (Fragment) node.getValue();
                for (FlowElementInput input : fragment.getInputPorts()) {
                    for (FlowElementOutput pred : input.getOpposites()) {
                        List<FragmentNode> sources = nodes.get(pred);
                        if (sources == null) {
                            continue;
                        }
                        for (FragmentNode source : sources) {
                            source.addDownstream(pred, node);
                            graph.addEdge(source, node);
                        }
                    }
                }
            }
        }
    }

    private void buildOutputGraph(
            Map<FlowElementOutput, List<FragmentNode>> nodes,
            Graph<FragmentNode> graph) {
        assert nodes != null;
        assert graph != null;
        for (Sink sink : stage.getStageResults()) {
            SimpleName name = names.create("output");
            FragmentNode node = new FragmentNode(Kind.OUTPUT, sink, name);
            for (FlowBlock.Output output : sink.getOutputs()) {
                FlowElementOutput target = output.getElementPort();
                List<FragmentNode> sources = nodes.get(target);
                if (sources == null) {
                    continue;
                }
                for (FragmentNode source : sources) {
                    source.addDownstream(target, node);
                    graph.addEdge(source, node);
                }
            }
        }
    }

    private void buildShuffleGraph(
            Map<FlowElementOutput, List<FragmentNode>> nodes,
            Graph<FragmentNode> graph) {
        assert nodes != null;
        assert graph != null;
        assert shuffle != null;
        assert stage.getStageBlock().hasReduceBlocks();

        Map<FlowElementInput, FlowBlock.Input> inputMap = Maps.create();
        for (FlowBlock reduceBlock : stage.getStageBlock().getReduceBlocks()) {
            for (FlowBlock.Input blockInput : reduceBlock.getBlockInputs()) {
                assert inputMap.containsKey(blockInput.getElementPort()) == false;
                inputMap.put(blockInput.getElementPort(), blockInput);
            }
        }

        for (ShuffleModel.Segment segment : shuffle.getSegments()) {
            FlowElementInput input = segment.getPort();
            FlowBlock.Input blockInput = inputMap.get(input);
            if (blockInput == null) {
                continue;
            }
            FragmentNode node = new FragmentNode(Kind.SHUFFLE, segment, names.create("shuffle"));
            for (FlowBlock.Connection conn : blockInput.getConnections()) {
                FlowElementOutput shuffleOut = conn.getUpstream().getElementPort();
                List<FragmentNode> sources = nodes.get(shuffleOut);
                if (sources == null) {
                    continue;
                }
                for (FragmentNode source : sources) {
                    source.addDownstream(shuffleOut, node);
                    graph.addEdge(source, node);
                }
            }
        }
    }

    /**
     * 処理断片の先頭要素を参照するためのフィールド群を構築して返す。
     * @return 処理断片の先頭要素を参照するためのフィールド群
     */
    public List<FieldDeclaration> createFields() {
        List<FieldDeclaration> results = Lists.create();
        if (stageOutputs != null) {
            results.add(createStageOutputsField());
        }
        for (Map.Entry<ResourceFragment, SimpleName> entry : resources.entrySet()) {
            results.add(createResourceField(entry.getKey(), entry.getValue()));
        }
        for (FragmentNode node : lines.values()) {
            results.add(createFragmentField(node, (StageModel.Fragment) node.getValue()));
        }
        for (FragmentNode node : rendezvous.values()) {
            results.add(createFragmentField(node, (StageModel.Fragment) node.getValue()));
        }
        return results;
    }

    private FieldDeclaration createResourceField(ResourceFragment resource, SimpleName name) {
        assert resource != null;
        assert name != null;
        FieldDeclaration field = factory.newFieldDeclaration(
                null,
                new AttributeBuilder(factory)
                    .Private()
                    .toAttributes(),
                importer.toType(resource.getCompiled().getQualifiedName()),
                name,
                null);
        return field;
    }

    private FieldDeclaration createStageOutputsField() {
        FieldDeclaration field = factory.newFieldDeclaration(
                null,
                new AttributeBuilder(factory)
                    .Private()
                    .toAttributes(),
                importer.toType(StageOutputDriver.class),
                stageOutputs,
                null);
        return field;
    }

    private FieldDeclaration createFragmentField(FragmentNode node, StageModel.Fragment value) {
        assert node != null;
        assert value != null;
        Type type = importer.resolve(factory.newNamedType(value.getCompiled().getQualifiedName()));
        return factory.newFieldDeclaration(
                null,
                new AttributeBuilder(factory)
                    .Private()
                    .toAttributes(),
                type,
                node.getName(),
                null);
    }

    /**
     * 処理断片を初期化する文の一覧を返す。
     * @param context コンテキストオブジェクトを参照するための式
     * @return 構築した文の一覧
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public List<Statement> createSetup(Expression context) {
        Precondition.checkMustNotBeNull(context, "context"); //$NON-NLS-1$
        List<Statement> results = Lists.create();
        if (stageOutputs != null) {
            results.addAll(setupStageOutputs(context));
        }
        results.addAll(setupResources(context));
        results.addAll(setupFragments(context));
        return results;
    }

    private List<Statement> setupResources(Expression context) {
        assert context != null;
        List<Statement> results = Lists.create();
        for (Map.Entry<ResourceFragment, SimpleName> entry : resources.entrySet()) {
            ResourceFragment resource = entry.getKey();
            SimpleName field = entry.getValue();
            results.add(new ExpressionBuilder(factory, factory.newThis())
                .field(field)
                .assignFrom(new TypeBuilder(factory, importer.toType(resource.getCompiled().getQualifiedName()))
                    .newObject()
                    .toExpression())
                .toStatement());
            results.add(new ExpressionBuilder(factory, factory.newThis())
                .field(field)
                .method("setup", new ExpressionBuilder(factory, context)
                    .method("getConfiguration")
                    .toExpression())
                .toStatement());
        }
        return results;
    }

    private List<Statement> setupFragments(Expression context) {
        List<Statement> results = Lists.create();
        for (FragmentNode node : Graphs.sortPostOrder(dependencies)) {
            switch (node.getKind()) {
            case LINE:
                results.add(setupLine(node, (StageModel.Fragment) node.getValue()));
                break;
            case RENDEZVOUS:
                results.add(setupRendezvous(node, (StageModel.Fragment) node.getValue()));
                break;
            case SHUFFLE:
                results.add(setupShuffle(node, context, (ShuffleModel.Segment) node.getValue()));
                break;
            case OUTPUT:
                results.add(setupOutput(node, (StageModel.Sink) node.getValue()));
                break;
            default:
                throw new AssertionError(node);
            }
        }
        return results;
    }

    private List<Statement> setupStageOutputs(Expression context) {
        assert context != null;
        List<Statement> results = Lists.create();
        results.add(new ExpressionBuilder(factory, factory.newThis())
            .field(stageOutputs)
            .assignFrom(new TypeBuilder(factory, importer.toType(StageOutputDriver.class))
                .newObject(context)
                .toExpression())
            .toStatement());
        return results;
    }

    private Statement setupLine(FragmentNode node, StageModel.Fragment value) {
        assert node != null;
        assert value != null;
        assert value.getInputPorts().size() == 1;
        FlowElementInput input = value.getInputPorts().get(0);
        Type type = importer.resolve(factory.newNamedType(value.getCompiled().getQualifiedName()));
        List<Expression> arguments = resolveArguments(node, value);
        if (lines.containsKey(input)) {
            return new ExpressionBuilder(factory, factory.newThis())
                .field(node.getName())
                .assignFrom(new TypeBuilder(factory, type)
                    .newObject(arguments)
                    .toExpression())
                .toStatement();
        } else {
            return factory.newLocalVariableDeclaration(
                    new AttributeBuilder(factory)
                        .Final()
                        .toAttributes(),
                    type,
                    Collections.singletonList(factory.newVariableDeclarator(
                            node.getName(),
                            new TypeBuilder(factory, type)
                                .newObject(arguments)
                                .toExpression())));
        }
    }

    private Statement setupRendezvous(FragmentNode node, StageModel.Fragment value) {
        assert node != null;
        assert value != null;
        assert value.getInputPorts().isEmpty() == false;
        FlowElement element = value.getInputPorts().get(0).getOwner();
        Type type = importer.resolve(factory.newNamedType(value.getCompiled().getQualifiedName()));
        List<Expression> arguments = resolveArguments(node, value);
        assert rendezvous.containsKey(element);
        return new ExpressionBuilder(factory, factory.newThis())
            .field(node.getName())
            .assignFrom(new TypeBuilder(factory, type)
                .newObject(arguments)
                .toExpression())
            .toStatement();
    }

    private List<Expression> resolveArguments(FragmentNode node, StageModel.Fragment fragment) {
        assert node != null;
        assert fragment != null;
        List<Expression> results = Lists.create();
        // TODO 引数の並び順に暗黙の前提: リソース一覧 -> 出力一覧
        for (ResourceFragment resource : fragment.getResources()) {
            results.add(resolveResouce(resource));
        }
        for (FlowElementOutput output : fragment.getOutputPorts()) {
            results.add(resolveArgument(
                    output.getDescription().getDataType(),
                    node.getDownstream(output)));
        }
        return results;
    }

    private Expression resolveResouce(ResourceFragment resource) {
        assert resource != null;
        SimpleName name = resources.get(resource);
        assert name != null;
        return name;
    }

    private Expression resolveArgument(java.lang.reflect.Type type, Set<FragmentNode> downstream) {
        assert type != null;
        assert downstream != null;
        if (downstream.isEmpty()) {
            return new TypeBuilder(factory, importer.resolve(
                    factory.newParameterizedType(
                            Models.toType(factory, VoidResult.class),
                            Collections.singletonList(Models.toType(factory, type)))))
                .newObject()
                .toExpression();
        }
        if (downstream.size() == 1) {
            FragmentNode succ = downstream.iterator().next();
            return succ.getName();
        }

        DataClass model = environment.getDataClasses().load(type);
        if (model == null) {
            throw new IllegalStateException(type.toString());
        }
        Type dataType = importer.toType(model.getType());

        SimpleName cacheName = names.create("cache");
        FieldDeclaration cache = factory.newFieldDeclaration(
                null,
                new AttributeBuilder(factory)
                    .Private()
                    .toAttributes(),
                dataType,
                cacheName,
                model.createNewInstance(dataType));

        SimpleName argumentName = names.create("arg");
        List<Statement> statements = Lists.create();
        Iterator<FragmentNode> iter = downstream.iterator();
        while (iter.hasNext()) {
            FragmentNode node = iter.next();
            if (iter.hasNext()) {
                statements.add(model.assign(cacheName, argumentName));
                statements.add(new ExpressionBuilder(factory, node.getName())
                    .method(FlowElementProcessor.RESULT_METHOD_NAME, cacheName)
                    .toStatement());
            } else {
                statements.add(new ExpressionBuilder(factory, node.getName())
                    .method(FlowElementProcessor.RESULT_METHOD_NAME, argumentName)
                    .toStatement());
            }
        }

        MethodDeclaration result = factory.newMethodDeclaration(
                null,
                new AttributeBuilder(factory)
                    .annotation(importer.toType(Override.class))
                    .Public()
                    .toAttributes(),
                factory.newBasicType(BasicTypeKind.VOID),
                factory.newSimpleName(FlowElementProcessor.RESULT_METHOD_NAME),
                Collections.singletonList(factory.newFormalParameterDeclaration(
                        Models.toType(factory, model.getType()),
                        argumentName)),
                statements);

        return factory.newClassInstanceCreationExpression(
                null,
                Collections.<Type>emptyList(),
                importer.resolve(factory.newParameterizedType(
                        Models.toType(factory, Result.class),
                        dataType)),
                Collections.<Expression>emptyList(),
                factory.newClassBody(Arrays.asList(cache, result)));
    }

    private Statement setupShuffle(
            FragmentNode node,
            Expression context,
            ShuffleModel.Segment value) {
        assert node != null;
        assert context != null;
        assert value != null;
        Type type = importer.toType(value.getCompiled().getMapOutputType().getQualifiedName());
        return factory.newLocalVariableDeclaration(
                new AttributeBuilder(factory)
                    .Final()
                    .toAttributes(),
                type,
                Collections.singletonList(factory.newVariableDeclarator(
                        node.getName(),
                        new TypeBuilder(factory, type)
                            .newObject(context)
                            .toExpression())));
    }

    private Statement setupOutput(FragmentNode node, StageModel.Sink value) {
        assert node != null;
        assert value != null;
        Type type = importer.resolve(factory.newParameterizedType(
                Models.toType(factory, Result.class),
                Models.toType(factory, value.getType())));
        return factory.newLocalVariableDeclaration(
                new AttributeBuilder(factory)
                    .Final()
                    .toAttributes(),
                type,
                Collections.singletonList(factory.newVariableDeclarator(
                        node.getName(),
                        new ExpressionBuilder(factory, stageOutputs)
                            .method("getResultSink", Models.toLiteral(factory, value.getName()))
                            .toExpression())));
    }

    /**
     * 処理断片を破棄する文の一覧を返す。
     * @param context コンテキストオブジェクトを参照するための式
     * @return 構築した文の一覧
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public List<Statement> createCleanup(SimpleName context) {
        Precondition.checkMustNotBeNull(context, "context"); //$NON-NLS-1$
        List<Statement> results = Lists.create();
        if (stageOutputs != null) {
            results.addAll(cleanStageOutputs(context));
        }
        for (Map.Entry<ResourceFragment, SimpleName> entry : resources.entrySet()) {
            results.add(factory.newIfStatement(
                    new ExpressionBuilder(factory, factory.newThis())
                        .field(entry.getValue())
                        .apply(InfixOperator.NOT_EQUALS, Models.toNullLiteral(factory))
                        .toExpression(),
                    factory.newBlock(new Statement[] {
                            new ExpressionBuilder(factory, factory.newThis())
                                .field(entry.getValue())
                                .method("cleanup", new ExpressionBuilder(factory, context)
                                    .method("getConfiguration")
                                    .toExpression())
                                .toStatement(),
                            new ExpressionBuilder(factory, factory.newThis())
                                .field(entry.getValue())
                                .assignFrom(Models.toNullLiteral(factory))
                                .toStatement()
                    })));
        }
        for (FragmentNode node : lines.values()) {
            results.add(new ExpressionBuilder(factory, factory.newThis())
                .field(node.getName())
                .assignFrom(Models.toNullLiteral(factory))
                .toStatement());
        }
        for (FragmentNode node : rendezvous.values()) {
            results.add(new ExpressionBuilder(factory, factory.newThis())
                .field(node.getName())
                .assignFrom(Models.toNullLiteral(factory))
                .toStatement());
        }
        return results;
    }

    private List<Statement> cleanStageOutputs(SimpleName context) {
        assert context != null;
        List<Statement> results = Lists.create();
        results.add(new ExpressionBuilder(factory, factory.newThis())
            .field(stageOutputs)
            .method("close")
            .toStatement());
        results.add(new ExpressionBuilder(factory, factory.newThis())
            .field(stageOutputs)
            .assignFrom(Models.toNullLiteral(factory))
            .toStatement());
        return results;
    }

    /**
     * シャッフル出力に関するキーの型を返す。
     * @return シャッフル出力に関するキーの型、シャッフルしない場合はダミーの型
     */
    public Type getShuffleKeyType() {
        if (shuffle == null) {
            return importer.toType(NullWritable.class);
        }
        Name keyName = shuffle.getCompiled().getKeyTypeName();
        return importer.resolve(factory.newNamedType(keyName));
    }

    /**
     * シャッフル出力に関する値の型を返す。
     * @return シャッフル出力に関する値の型、シャッフルしない場合はダミーの型
     */
    public Type getShuffleValueType() {
        if (shuffle == null) {
            return importer.toType(NullWritable.class);
        }
        Name valueName = shuffle.getCompiled().getValueTypeName();
        return importer.resolve(factory.newNamedType(valueName));
    }

    /**
     * 指定の入力に関連するライン断片を参照するための式を返す。
     * @param input 対象の入力
     * @return 対応する式
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public Expression getLine(FlowElementInput input) {
        Precondition.checkMustNotBeNull(input, "input"); //$NON-NLS-1$
        FragmentNode node = lines.get(input);
        if (node == null) {
            throw new IllegalArgumentException();
        }
        return new ExpressionBuilder(factory, factory.newThis())
            .field(node.getName())
            .toExpression();
    }

    /**
     * 指定の要素に関連する合流断片を参照するための式を返す。
     * @param element 対象の要素
     * @return 対応する式
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public Expression getRendezvous(FlowElement element) {
        Precondition.checkMustNotBeNull(element, "element"); //$NON-NLS-1$
        FragmentNode node = rendezvous.get(element);
        if (node == null) {
            throw new IllegalArgumentException();
        }
        return new ExpressionBuilder(factory, factory.newThis())
            .field(node.getName())
            .toExpression();
    }

    private static class FragmentNode {

        private final Kind kind;

        private final Object value;

        private final SimpleName name;

        private final Map<FlowElementOutput, Set<FragmentNode>> downstreams;

        FragmentNode(Kind kind, Object value, SimpleName name) {
            assert kind != null;
            assert value != null;
            assert name != null;
            this.kind = kind;
            this.value = value;
            this.name = name;
            this.downstreams = Maps.create();
        }

        public void addDownstream(FlowElementOutput output, FragmentNode downstream) {
            assert output != null;
            assert downstream != null;
            Maps.addToSet(downstreams, output, downstream);
        }

        public Set<FragmentNode> getDownstream(FlowElementOutput output) {
            assert output != null;
            Set<FragmentNode> set = downstreams.get(output);
            if (set == null) {
                return Collections.emptySet();
            }
            return set;
        }

        public Kind getKind() {
            return kind;
        }

        public Object getValue() {
            return value;
        }

        public SimpleName getName() {
            return name;
        }
    }

    /**
     * ノードの種類。
     */
    public enum Kind {

        /**
         * 1入力多出力のライン。
         * <p>
         * コンパイル結果が{@link Result}型を表す{@link StageModel.Fragment}
         * コンストラクターの引数に、出力の順にノードを取る。
         * </p>
         */
        LINE,

        /**
         * 合流を表す演算子。
         * <p>
         * コンパイル結果が{@link Rendezvous}型を表す{@link StageModel.Fragment}
         * コンストラクターの引数に、出力の順にノードを取る。
         * </p>
         */
        RENDEZVOUS,

        /**
         * 1入力をシャッフルに出力する。
         * <p>
         * コンパイル結果が{@link Result}型を表す{@link ShuffleModel.Segment}
         * コンストラクターの引数に、{@link TaskInputOutputContext
         * TaskInputOutputContext<?, ?, ? super K, ? super V>}を取る。
         * </p>
         */
        SHUFFLE,

        /**
         * 1入力を既定のファイルシステムに出力する。
         * <p>
         * シャッフル以外へ出力する{@link StageModel.Sink}
         * </p>
         */
        OUTPUT,
    }
}
TOP

Related Classes of com.asakusafw.compiler.flow.stage.FragmentFlow$FragmentNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.