Package com.asakusafw.compiler.directio.emitter

Source Code of com.asakusafw.compiler.directio.emitter.StageEmitter

/**
* 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.directio.emitter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.hadoop.io.NullWritable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.asakusafw.compiler.common.Naming;
import com.asakusafw.compiler.common.Precondition;
import com.asakusafw.compiler.flow.ExternalIoDescriptionProcessor.SourceInfo;
import com.asakusafw.compiler.flow.FlowCompilingEnvironment;
import com.asakusafw.compiler.flow.Location;
import com.asakusafw.compiler.flow.jobflow.CompiledStage;
import com.asakusafw.runtime.directio.DirectDataSourceConstants;
import com.asakusafw.runtime.io.util.ShuffleKey.AbstractGroupComparator;
import com.asakusafw.runtime.io.util.ShuffleKey.AbstractOrderComparator;
import com.asakusafw.runtime.io.util.ShuffleKey.Partitioner;
import com.asakusafw.runtime.stage.AbstractStageClient;
import com.asakusafw.runtime.stage.BaseStageClient;
import com.asakusafw.runtime.stage.StageInput;
import com.asakusafw.runtime.stage.StageOutput;
import com.asakusafw.runtime.stage.directio.AbstractDirectOutputKey;
import com.asakusafw.runtime.stage.directio.AbstractDirectOutputMapper;
import com.asakusafw.runtime.stage.directio.AbstractDirectOutputValue;
import com.asakusafw.runtime.stage.directio.AbstractNoReduceDirectOutputMapper;
import com.asakusafw.runtime.stage.directio.DirectOutputReducer;
import com.asakusafw.runtime.stage.directio.DirectOutputSpec;
import com.asakusafw.runtime.stage.output.BridgeOutputFormat;
import com.asakusafw.runtime.trace.TraceLocation;
import com.asakusafw.utils.collections.Lists;
import com.asakusafw.utils.java.model.syntax.ArrayType;
import com.asakusafw.utils.java.model.syntax.ClassDeclaration;
import com.asakusafw.utils.java.model.syntax.Comment;
import com.asakusafw.utils.java.model.syntax.CompilationUnit;
import com.asakusafw.utils.java.model.syntax.ConstructorDeclaration;
import com.asakusafw.utils.java.model.syntax.Expression;
import com.asakusafw.utils.java.model.syntax.FormalParameterDeclaration;
import com.asakusafw.utils.java.model.syntax.Javadoc;
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.QualifiedName;
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.syntax.TypeBodyDeclaration;
import com.asakusafw.utils.java.model.syntax.TypeDeclaration;
import com.asakusafw.utils.java.model.syntax.TypeParameterDeclaration;
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.ImportBuilder.Strategy;
import com.asakusafw.utils.java.model.util.JavadocBuilder;
import com.asakusafw.utils.java.model.util.Models;
import com.asakusafw.utils.java.model.util.TypeBuilder;

/**
* Emits a stage class for direct output.
* @since 0.2.5
*/
public class StageEmitter {

    static final Logger LOG = LoggerFactory.getLogger(StageEmitter.class);

    private final FlowCompilingEnvironment environment;

    private final String moduleId;

    /**
     * Creates a new instance.
     * @param environment current environment
     * @param moduleId target module ID
     * @throws IllegalArgumentException if some parameters were {@code null}
     */
    public StageEmitter(FlowCompilingEnvironment environment, String moduleId) {
        Precondition.checkMustNotBeNull(environment, "environment"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(moduleId, "moduleId"); //$NON-NLS-1$
        this.environment = environment;
        this.moduleId = moduleId;
    }

    /**
     * Emits a client class.
     * @param slots target slots
     * @param outputLocation output location
     * @return the generated class name
     * @throws IOException if failed to emit class
     * @throws IllegalArgumentException if some parameters were {@code null}
     */
    public CompiledStage emit(List<Slot> slots, Location outputLocation) throws IOException {
        Precondition.checkMustNotBeNull(slots, "slots"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(outputLocation, "outputLocation"); //$NON-NLS-1$
        LOG.debug("Start preparing output stage for Direct I/O epilogue: batch={}, flow={}",
                environment.getBatchId(),
                environment.getFlowId());
        if (requiresReducer(slots)) {
            return emitClientWithReducer(slots, outputLocation);
        } else {
            return emitClientWithoutReducer(slots, outputLocation);
        }
    }

    private boolean requiresReducer(List<Slot> slots) {
        assert slots != null;
        for (Slot slot : slots) {
            if (requiresReducer(slot)) {
                return true;
            }
        }
        return false;
    }

    private boolean requiresReducer(Slot slot) {
        assert slot != null;
        return slot.orderClass != null;
    }

    private CompiledStage emitClientWithReducer(List<Slot> slots, Location outputLocation) throws IOException {
        assert slots != null;
        assert outputLocation != null;
        LOG.debug("Emitting shuffle key for Direct I/O epilogue");
        Name key = emitKey(slots);

        LOG.debug("Emitting shuffle value for Direct I/O epilogue");
        Name value = emitValue(slots);

        LOG.debug("Emitting grouping comparator for Direct I/O epilogue");
        Name grouping = emitGrouping(key);

        LOG.debug("Emitting sort comparator for Direct I/O epilogue");
        Name ordering = emitOrdering(key);

        LOG.debug("Emitting mappers for Direct I/O epilogue");
        List<CompiledSlot> compiledSlots = emitMappers(slots, key, value);

        LOG.debug("Emitting stage client (with reducer) for Direct I/O epilogue");
        Name client = emitClient(compiledSlots, key, value, grouping, ordering, outputLocation);

        LOG.debug("Finish preparing output stage for Direct I/O epilogue: batch={}, flow={}, class={}", new Object[] {
                environment.getBatchId(),
                environment.getFlowId(),
                client.toNameString(),
        });
        return new CompiledStage(client, Naming.getEpilogueName(moduleId));
    }

    private CompiledStage emitClientWithoutReducer(List<Slot> slots, Location outputLocation) throws IOException {
        assert slots != null;
        assert outputLocation != null;

        LOG.debug("Emitting mappers for Direct I/O epilogue");
        List<CompiledSlot> compiledSlots = emitMappers(slots, null, null);

        LOG.debug("Emitting stage client (without reducer) for Direct I/O epilogue");
        Name client = emitClient(compiledSlots, null, null, null, null, outputLocation);

        LOG.debug("Finish preparing output stage for Direct I/O epilogue: batch={}, flow={}, class={}", new Object[] {
                environment.getBatchId(),
                environment.getFlowId(),
                client.toNameString(),
        });
        return new CompiledStage(client, Naming.getEpilogueName(moduleId));
    }

    private Name emitKey(List<Slot> slots) throws IOException {
        assert slots != null;
        return emitWithSpecs(
                Naming.getShuffleKeyClass(),
                AbstractDirectOutputKey.class,
                slots);
    }

    private Name emitValue(List<Slot> slots) throws IOException {
        assert slots != null;
        return emitWithSpecs(
                Naming.getShuffleValueClass(),
                AbstractDirectOutputValue.class,
                slots);
    }

    private Name emitGrouping(Name key) throws IOException {
        assert key != null;
        return emitWithClass(
                Naming.getShuffleGroupingComparatorClass(),
                AbstractGroupComparator.class,
                key);
    }

    private Name emitOrdering(Name key) throws IOException {
        return emitWithClass(
                Naming.getShuffleSortComparatorClass(),
                AbstractOrderComparator.class,
                key);
    }

    private List<CompiledSlot> emitMappers(List<Slot> slots, Name keyOrNull, Name valueOrNull) throws IOException {
        assert slots != null;
        List<CompiledSlot> results = Lists.create();
        int index = 0;
        for (Slot slot : slots) {
            Name mapper;
            if (requiresReducer(slot)) {
                assert keyOrNull != null;
                assert valueOrNull != null;
                mapper = emitShuffleMapper(slot, index, keyOrNull, valueOrNull);
            } else {
                mapper = emitOutputMapper(slot, index);
            }
            results.add(new CompiledSlot(slot, mapper));
            index++;
        }
        return results;
    }

    private Name emitShuffleMapper(Slot slot, int index, Name key, Name value) throws IOException {
        assert slot != null;
        assert key != null;
        assert value != null;
        assert index >= 0;
        assert requiresReducer(slot);
        ModelFactory f = environment.getModelFactory();
        SimpleName className = f.newSimpleName(Naming.getMapClass(index));
        ImportBuilder importer = new ImportBuilder(
                f,
                f.newPackageDeclaration(environment.getEpiloguePackageName(moduleId)),
                Strategy.TOP_LEVEL);
        importer.resolvePackageMember(className);
        List<Expression> arguments = Lists.create();
        arguments.add(Models.toLiteral(f, index));
        arguments.add(classLiteralOrNull(f, importer, key));
        arguments.add(classLiteralOrNull(f, importer, value));
        return emitConstructorClass(
                className,
                f.newParameterizedType(
                        importer.toType(AbstractDirectOutputMapper.class),
                        importer.toType(slot.valueType)),
                importer,
                arguments);
    }

    private Name emitOutputMapper(Slot slot, int index) throws IOException {
        assert slot != null;
        assert index >= 0;
        assert requiresReducer(slot) == false;
        ModelFactory f = environment.getModelFactory();
        SimpleName className = f.newSimpleName(Naming.getMapClass(index));
        ImportBuilder importer = new ImportBuilder(
                f,
                f.newPackageDeclaration(environment.getEpiloguePackageName(moduleId)),
                Strategy.TOP_LEVEL);
        importer.resolvePackageMember(className);
        List<Expression> arguments = Lists.create();
        arguments.add(f.newClassLiteral(importer.toType(slot.valueType)));
        arguments.add(Models.toLiteral(f, slot.basePath));
        arguments.add(Models.toLiteral(f, slot.resourcePath));
        arguments.add(f.newClassLiteral(importer.toType(slot.formatClass)));

        return emitConstructorClass(
                className,
                f.newParameterizedType(
                        importer.toType(AbstractNoReduceDirectOutputMapper.class),
                        importer.toType(slot.valueType)),
                importer,
                arguments);
    }

    private Name emitWithSpecs(String classNameString, Class<?> baseClass, List<Slot> slots) throws IOException {
        assert classNameString != null;
        assert baseClass != null;
        assert slots != null;
        ModelFactory f = environment.getModelFactory();
        SimpleName className = f.newSimpleName(classNameString);
        ImportBuilder importer = new ImportBuilder(
                f,
                f.newPackageDeclaration(environment.getEpiloguePackageName(moduleId)),
                Strategy.TOP_LEVEL);
        importer.resolvePackageMember(className);
        List<Expression> elements = Lists.create();
        for (Slot slot : slots) {
            if (requiresReducer(slot)) {
                List<Expression> arguments = Lists.create();
                arguments.add(f.newClassLiteral(importer.toType(slot.valueType)));
                arguments.add(Models.toLiteral(f, slot.basePath));
                arguments.add(f.newClassLiteral(importer.toType(slot.formatClass)));
                arguments.add(f.newClassLiteral(importer.toType(slot.namingClass)));
                arguments.add(f.newClassLiteral(importer.toType(slot.orderClass)));
                elements.add(new TypeBuilder(f, importer.toType(DirectOutputSpec.class))
                    .newObject(arguments)
                    .toExpression());
            } else {
                elements.add(Models.toNullLiteral(f));
            }
        }
        return emitConstructorClass(
                className,
                importer.toType(baseClass),
                importer,
                Collections.singletonList(f.newArrayCreationExpression(
                        (ArrayType) importer.toType(DirectOutputSpec[].class),
                        f.newArrayInitializer(elements))));
    }

    private Expression classLiteralOrNull(ModelFactory f, ImportBuilder importer, Name nameOrNull) {
        assert f != null;
        assert importer != null;
        if (nameOrNull == null) {
            return Models.toNullLiteral(f);
        } else {
            return f.newClassLiteral(importer.toType(nameOrNull));
        }
    }

    private Name emitWithClass(
            String classNameString,
            Class<?> baseClass,
            Name argumentClassName) throws IOException {
        assert classNameString != null;
        assert baseClass != null;
        assert argumentClassName != null;
        ModelFactory f = environment.getModelFactory();
        SimpleName className = f.newSimpleName(classNameString);
        ImportBuilder importer = new ImportBuilder(
                f,
                f.newPackageDeclaration(environment.getEpiloguePackageName(moduleId)),
                Strategy.TOP_LEVEL);
        importer.resolvePackageMember(className);
        List<Expression> arguments = Lists.create();
        arguments.add(classLiteralOrNull(f, importer, argumentClassName));
        return emitConstructorClass(className, importer.toType(baseClass), importer, arguments);
    }

    private Name emitConstructorClass(
            SimpleName className,
            Type baseClass,
            ImportBuilder importer,
            List<? extends Expression> arguments) throws IOException {
        assert className != null;
        assert importer != null;
        assert arguments != null;
        ModelFactory f = environment.getModelFactory();
        Statement ctorChain = f.newSuperConstructorInvocation(arguments);
        ConstructorDeclaration ctorDecl = f.newConstructorDeclaration(
                new JavadocBuilder(f)
                    .text("Creates a new instance.")
                    .toJavadoc(),
                new AttributeBuilder(f)
                    .Public()
                    .toAttributes(),
                className,
                Collections.<FormalParameterDeclaration>emptyList(),
                Collections.singletonList(ctorChain));
        ClassDeclaration typeDecl = f.newClassDeclaration(
                new JavadocBuilder(f)
                    .toJavadoc(),
                new AttributeBuilder(f)
                    .annotation(importer.toType(TraceLocation.class), createTraceLocationElements())
                    .Public()
                    .Final()
                    .toAttributes(),
                className,
                importer.resolve(baseClass),
                Collections.<Type>emptyList(),
                Collections.singletonList(ctorDecl));
        CompilationUnit source = f.newCompilationUnit(
                importer.getPackageDeclaration(),
                importer.toImportDeclarations(),
                Collections.singletonList(typeDecl),
                Collections.<Comment>emptyList());
        environment.emit(source);
        Name packageName = source.getPackageDeclaration().getName();
        SimpleName simpleName = source.getTypeDeclarations().get(0).getName();
        QualifiedName name = environment
            .getModelFactory()
            .newQualifiedName(packageName, simpleName);
        LOG.debug("epilogue of \"{}\" will use {}", moduleId, name);
        return name;
    }

    private Map<String, Expression> createTraceLocationElements() {
        ModelFactory factory = environment.getModelFactory();
        Map<String, Expression> results = new LinkedHashMap<String, Expression>();
        results.put("batchId", Models.toLiteral(factory, environment.getBatchId()));
        results.put("flowId", Models.toLiteral(factory, environment.getFlowId()));
        results.put("stageId", Models.toLiteral(factory, Naming.getEpilogueName(moduleId)));
        return results;
    }

    private Name emitClient(
            List<CompiledSlot> compiledSlots,
            Name keyOrNull, Name valueOrNull,
            Name groupingOrNull, Name orderingOrNull,
            Location outputLocation) throws IOException {
        assert compiledSlots != null;
        assert outputLocation != null;
        Name partitionerOrNull;
        Name reducerOrNull;
        if (keyOrNull != null) {
            partitionerOrNull = Models.toName(
                    environment.getModelFactory(), Partitioner.class.getName().replace('$', '.'));
            reducerOrNull = Models.toName(
                    environment.getModelFactory(), DirectOutputReducer.class.getName());
        } else {
            partitionerOrNull = null;
            reducerOrNull = null;
        }
        Engine engine = new Engine(
                environment,
                moduleId,
                compiledSlots,
                outputLocation,
                keyOrNull, valueOrNull,
                groupingOrNull, orderingOrNull, partitionerOrNull,
                reducerOrNull);
        CompilationUnit source = engine.generate();
        environment.emit(source);
        Name packageName = source.getPackageDeclaration().getName();
        SimpleName simpleName = source.getTypeDeclarations().get(0).getName();
        QualifiedName name = environment
            .getModelFactory()
            .newQualifiedName(packageName, simpleName);
        LOG.debug("\"{}\" will use {}", moduleId, name);
        return name;
    }

    private static class CompiledSlot {

        final Slot original;

        final Name mapperClass;

        CompiledSlot(Slot original, Name mapperClass) {
            this.original = original;
            this.mapperClass = mapperClass;
        }
    }

    private static class Engine {

        private static final char PATH_SEPARATOR = '/';

        private final FlowCompilingEnvironment environment;

        private final String moduleId;

        private final List<CompiledSlot> slots;

        private final Location outputDirectory;

        private final ModelFactory factory;

        private final ImportBuilder importer;

        private final Name key;

        private final Name value;

        private final Name grouping;

        private final Name ordering;

        private final Name partitioner;

        private final Name reducer;

        Engine(
                FlowCompilingEnvironment environment,
                String moduleId,
                List<CompiledSlot> slots,
                Location outputDirectory,
                Name key, Name value, Name grouping, Name ordering, Name partitioner, Name reducer) {
            assert environment != null;
            assert moduleId != null;
            assert slots != null;
            this.environment = environment;
            this.moduleId = moduleId;
            this.slots = slots;
            this.outputDirectory = outputDirectory;
            this.factory = environment.getModelFactory();
            Name packageName = environment.getEpiloguePackageName(moduleId);
            this.importer = new ImportBuilder(
                    factory,
                    factory.newPackageDeclaration(packageName),
                    ImportBuilder.Strategy.TOP_LEVEL);
            this.key = key;
            this.value = value;
            this.grouping = grouping;
            this.ordering = ordering;
            this.partitioner = partitioner;
            this.reducer = reducer;
        }

        public CompilationUnit generate() {
            TypeDeclaration type = createType();
            return factory.newCompilationUnit(
                    importer.getPackageDeclaration(),
                    importer.toImportDeclarations(),
                    Collections.singletonList(type),
                    Collections.<Comment>emptyList());
        }

        private TypeDeclaration createType() {
            SimpleName name = factory.newSimpleName(Naming.getClientClass());
            importer.resolvePackageMember(name);
            List<TypeBodyDeclaration> members = Lists.create();
            members.addAll(createIdMethods());
            members.add(createStageOutputPath());
            members.add(createStageInputsMethod());
            members.add(createStageOutputsMethod());
            if (key != null) {
                members.addAll(createShuffleMethods());
            }
            return factory.newClassDeclaration(
                    createJavadoc(),
                    new AttributeBuilder(factory)
                        .annotation(t(TraceLocation.class), createTraceLocationElements())
                        .Public()
                        .Final()
                        .toAttributes(),
                    name,
                    Collections.<TypeParameterDeclaration>emptyList(),
                    t(AbstractStageClient.class),
                    Collections.<Type>emptyList(),
                    members);
        }

        private Map<String, Expression> createTraceLocationElements() {
            Map<String, Expression> results = new LinkedHashMap<String, Expression>();
            results.put("batchId", Models.toLiteral(factory, environment.getBatchId()));
            results.put("flowId", Models.toLiteral(factory, environment.getFlowId()));
            results.put("stageId", Models.toLiteral(factory, Naming.getEpilogueName(moduleId)));
            return results;
        }

        private List<MethodDeclaration> createIdMethods() {
            List<MethodDeclaration> results = Lists.create();
            results.add(createValueMethod(
                    BaseStageClient.METHOD_BATCH_ID,
                    t(String.class),
                    Models.toLiteral(factory, environment.getBatchId())));
            results.add(createValueMethod(
                    BaseStageClient.METHOD_FLOW_ID,
                    t(String.class),
                    Models.toLiteral(factory, environment.getFlowId())));
            results.add(createValueMethod(
                    BaseStageClient.METHOD_STAGE_ID,
                    t(String.class),
                    Models.toLiteral(factory, Naming.getEpilogueName(moduleId))));
            return results;
        }

        private MethodDeclaration createStageOutputPath() {
            return createValueMethod(
                    AbstractStageClient.METHOD_STAGE_OUTPUT_PATH,
                    t(String.class),
                    Models.toLiteral(factory, outputDirectory.toPath(PATH_SEPARATOR)));
        }

        private MethodDeclaration createStageInputsMethod() {
            SimpleName list = factory.newSimpleName("results");
            SimpleName attributes = factory.newSimpleName("attributes");

            List<Statement> statements = Lists.create();
            statements.add(new TypeBuilder(factory, t(ArrayList.class, t(StageInput.class)))
                .newObject()
                .toLocalVariableDeclaration(t(List.class, t(StageInput.class)), list));
            statements.add(new ExpressionBuilder(factory, Models.toNullLiteral(factory))
                .toLocalVariableDeclaration(t(Map.class, t(String.class), t(String.class)), attributes));

            for (CompiledSlot slot : slots) {
                Type mapperType = importer.toType(slot.mapperClass);
                for (SourceInfo info : slot.original.sources) {
                    statements.add(new ExpressionBuilder(factory, attributes)
                        .assignFrom(new TypeBuilder(factory, t(HashMap.class, t(String.class), t(String.class)))
                            .newObject()
                            .toExpression())
                        .toStatement());
                    for (Map.Entry<String, String> entry : info.getAttributes().entrySet()) {
                        statements.add(new ExpressionBuilder(factory, attributes)
                            .method("put",
                                    Models.toLiteral(factory, entry.getKey()),
                                    Models.toLiteral(factory, entry.getValue()))
                            .toStatement());
                    }
                    for (Location input : info.getLocations()) {
                        statements.add(new ExpressionBuilder(factory, list)
                            .method("add", new TypeBuilder(factory, t(StageInput.class))
                                .newObject(
                                        Models.toLiteral(factory, input.toPath(PATH_SEPARATOR)),
                                        factory.newClassLiteral(t(info.getFormat())),
                                        factory.newClassLiteral(mapperType),
                                        attributes)
                                .toExpression())
                            .toStatement());
                    }
                }
            }
            statements.add(new ExpressionBuilder(factory, list)
                .toReturnStatement());

            return factory.newMethodDeclaration(
                    null,
                    new AttributeBuilder(factory)
                        .annotation(t(Override.class))
                        .Protected()
                        .toAttributes(),
                    t(List.class, t(StageInput.class)),
                    factory.newSimpleName(AbstractStageClient.METHOD_STAGE_INPUTS),
                    Collections.<FormalParameterDeclaration>emptyList(),
                    statements);
        }

        private List<MethodDeclaration> createShuffleMethods() {
            List<MethodDeclaration> results = Lists.create();
            results.add(createClassLiteralMethod(AbstractStageClient.METHOD_SHUFFLE_KEY_CLASS, key));
            results.add(createClassLiteralMethod(AbstractStageClient.METHOD_SHUFFLE_VALUE_CLASS, value));
            results.add(createClassLiteralMethod(AbstractStageClient.METHOD_GROUPING_COMPARATOR_CLASS, grouping));
            results.add(createClassLiteralMethod(AbstractStageClient.METHOD_SORT_COMPARATOR_CLASS, ordering));
            results.add(createClassLiteralMethod(AbstractStageClient.METHOD_PARTITIONER_CLASS, partitioner));
            results.add(createClassLiteralMethod(AbstractStageClient.METHOD_REDUCER_CLASS, reducer));
            return results;
        }

        private MethodDeclaration createClassLiteralMethod(
                String methodName,
                Name typeName) {
            assert methodName != null;
            Type type = importer.toType(typeName);
            return createValueMethod(methodName, t(Class.class, type), factory.newClassLiteral(type));
        }

        private MethodDeclaration createStageOutputsMethod() {
            SimpleName list = factory.newSimpleName("results");
            SimpleName attributes = factory.newSimpleName("attributes");

            List<Statement> statements = Lists.create();
            statements.add(new TypeBuilder(factory, t(ArrayList.class, t(StageOutput.class)))
                .newObject()
                .toLocalVariableDeclaration(t(List.class, t(StageOutput.class)), list));
            statements.add(new ExpressionBuilder(factory, Models.toNullLiteral(factory))
                .toLocalVariableDeclaration(t(Map.class, t(String.class), t(String.class)), attributes));

            Type formatType = t(BridgeOutputFormat.class);
            for (CompiledSlot slot : slots) {
                Slot origin = slot.original;
                Expression valueType = factory.newClassLiteral(importer.toType(origin.valueType));
                statements.add(new ExpressionBuilder(factory, attributes)
                    .assignFrom(new TypeBuilder(factory, t(HashMap.class, t(String.class), t(String.class)))
                        .newObject()
                        .toExpression())
                    .toStatement());
                int index = 1;
                for (String pattern : slot.original.deletePatterns) {
                    statements.add(new ExpressionBuilder(factory, attributes)
                        .method("put",
                                Models.toLiteral(factory, String.format(
                                        "%s%02d", DirectDataSourceConstants.PREFIX_DELETE_PATTERN, index++)),
                                Models.toLiteral(factory, pattern))
                        .toStatement());
                }
                statements.add(new ExpressionBuilder(factory, list)
                    .method("add", new TypeBuilder(factory, t(StageOutput.class))
                        .newObject(
                                Models.toLiteral(factory, origin.basePath),
                                factory.newClassLiteral(t(NullWritable.class)),
                                valueType,
                                factory.newClassLiteral(formatType),
                                attributes)
                        .toExpression())
                    .toStatement());
            }
            statements.add(new ExpressionBuilder(factory, list)
                .toReturnStatement());

            return factory.newMethodDeclaration(
                    null,
                    new AttributeBuilder(factory)
                        .annotation(t(Override.class))
                        .Protected()
                        .toAttributes(),
                    t(List.class, t(StageOutput.class)),
                    factory.newSimpleName(AbstractStageClient.METHOD_STAGE_OUTPUTS),
                    Collections.<FormalParameterDeclaration>emptyList(),
                    statements);
        }

        private Javadoc createJavadoc() {
            return new JavadocBuilder(factory)
                .text("A client class for \"{0}\".", moduleId)
                .toJavadoc();
        }

        private MethodDeclaration createValueMethod(
                String methodName,
                Type returnType,
                Expression expression) {
            return factory.newMethodDeclaration(
                    null,
                    new AttributeBuilder(factory)
                        .annotation(t(Override.class))
                        .Protected()
                        .toAttributes(),
                    returnType,
                    factory.newSimpleName(methodName),
                    Collections.<FormalParameterDeclaration>emptyList(),
                    Collections.singletonList(factory.newReturnStatement(expression)));
        }

        private Type t(java.lang.reflect.Type type, Type...typeArgs) {
            assert type != null;
            assert typeArgs != null;
            Type raw = importer.toType(type);
            if (typeArgs.length == 0) {
                return raw;
            }
            return factory.newParameterizedType(raw, Arrays.asList(typeArgs));
        }
    }
}
TOP

Related Classes of com.asakusafw.compiler.directio.emitter.StageEmitter

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.