Package com.asakusafw.compiler.operator.flow

Source Code of com.asakusafw.compiler.operator.flow.FlowFactoryClassGenerator

/**
* 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.operator.flow;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.annotation.Generated;

import com.asakusafw.compiler.common.JavaName;
import com.asakusafw.compiler.common.NameGenerator;
import com.asakusafw.compiler.operator.OperatorCompilingEnvironment;
import com.asakusafw.compiler.operator.OperatorPortDeclaration;
import com.asakusafw.compiler.operator.util.GeneratorUtil;
import com.asakusafw.utils.collections.Lists;
import com.asakusafw.utils.java.jsr269.bridge.Jsr269;
import com.asakusafw.utils.java.model.syntax.Expression;
import com.asakusafw.utils.java.model.syntax.FieldDeclaration;
import com.asakusafw.utils.java.model.syntax.FormalParameterDeclaration;
import com.asakusafw.utils.java.model.syntax.MethodDeclaration;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
import com.asakusafw.utils.java.model.syntax.ModelKind;
import com.asakusafw.utils.java.model.syntax.NamedType;
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.JavadocBuilder;
import com.asakusafw.utils.java.model.util.Models;
import com.asakusafw.utils.java.model.util.TypeBuilder;
import com.asakusafw.vocabulary.flow.FlowDescription;
import com.asakusafw.vocabulary.flow.FlowPart;
import com.asakusafw.vocabulary.flow.Operator;
import com.asakusafw.vocabulary.flow.graph.FlowElementResolver;
import com.asakusafw.vocabulary.flow.graph.FlowPartDescription;
import com.asakusafw.vocabulary.flow.graph.Inline;
import com.asakusafw.vocabulary.operator.OperatorFactory;
import com.asakusafw.vocabulary.operator.OperatorInfo;

/**
* フロー部品クラスから演算子ファクトリークラスのJava DOMを構築する。
*/
public class FlowFactoryClassGenerator {

    /**
     * {@link FlowElementResolver}を保持するフィールド名。
     */
    static final String RESOLVER_FIELD_NAME = "$";

    private final ModelFactory factory;

    private final ImportBuilder importer;

    private final FlowPartClass flowClass;

    private final GeneratorUtil util;

    private final OperatorCompilingEnvironment environment;

    /**
     * インスタンスを生成する。
     * @param environment 環境オブジェクト
     * @param factory DOMを構築するためのファクトリ
     * @param importer インポート宣言を構築するビルダー
     * @param flowClass フロー部品クラスの情報
     * @throws IllegalArgumentException 引数に{@code null}が含まれる場合
     */
    public FlowFactoryClassGenerator(
            OperatorCompilingEnvironment environment,
            ModelFactory factory,
            ImportBuilder importer,
            FlowPartClass flowClass) {
        this.environment = environment;
        this.factory = factory;
        this.importer = importer;
        this.flowClass = flowClass;
        this.util = new GeneratorUtil(environment, factory, importer);
    }

    /**
     * このジェネレータの情報を利用して型宣言の情報を生成する。
     * @return 生成したモデル
     */
    public TypeDeclaration generate() {
        // 先に名前空間を退避する
        SimpleName name = getClassName();
        importer.resolvePackageMember(Models.append(
                factory,
                name,
                getObjectClassName()));
        return factory.newClassDeclaration(
                new JavadocBuilder(factory)
                    .code(flowClass.getElement().getSimpleName().toString())
                    .text("に対する演算子ファクトリークラス。")
                    .seeType(new Jsr269(factory).convert(environment.getErasure(flowClass.getElement().asType())))
                    .toJavadoc(),
                new AttributeBuilder(factory)
                    .annotation(util.t(Generated.class), util.v("{0}:{1}",
                            FlowOperatorCompiler.class.getSimpleName(),
                            FlowOperatorCompiler.VERSION))
                    .annotation(util.t(OperatorFactory.class),
                            factory.newClassLiteral(util.t(flowClass.getElement())))
                    .Public()
                    .toAttributes(),
                name,
                Collections.<TypeParameterDeclaration>emptyList(),
                null,
                Collections.<Type>emptyList(),
                createMembers());
    }

    private List<TypeBodyDeclaration> createMembers() {
        List<TypeBodyDeclaration> results = Lists.create();

        TypeDeclaration objectClass = createObjectClass();
        results.add(objectClass);

        NamedType objectType = (NamedType) importer.resolvePackageMember(
                Models.append(factory,
                        getClassName(),
                        objectClass.getName()));
        MethodDeclaration factoryMethod = createFactoryMethod(objectType);
        if (factoryMethod != null) {
            results.add(factoryMethod);
        }

        return results;
    }

    private TypeDeclaration createObjectClass() {
        SimpleName name = getObjectClassName();
        NamedType objectType = (NamedType) importer.resolvePackageMember(
                Models.append(factory, getClassName(), name));
        List<TypeBodyDeclaration> members = createObjectMembers(objectType);
        return factory.newClassDeclaration(
                new JavadocBuilder(factory)
                    .inline(flowClass.getDocumentation())
                    .seeType(new Jsr269(factory).convert(environment.getErasure(flowClass.getElement().asType())))
                    .toJavadoc(),
                new AttributeBuilder(factory)
                    .Public()
                    .Static()
                    .Final()
                    .toAttributes(),
                name,
                util.toTypeParameters(flowClass.getElement()),
                null,
                Collections.singletonList(util.t(Operator.class)),
                members);
    }

    private SimpleName getObjectClassName() {
        return factory.newSimpleName(
                JavaName.of(flowClass.getElement().getSimpleName().toString()).toTypeName());
    }

    private List<TypeBodyDeclaration> createObjectMembers(NamedType objectType) {
        assert objectType != null;
        NameGenerator names = new NameGenerator(factory);
        List<TypeBodyDeclaration> results = Lists.create();
        results.add(createResolverField());
        for (OperatorPortDeclaration var : flowClass.getOutputPorts()) {
            results.add(createObjectOutputField(var, names));
        }
        results.add(createObjectConstructor(objectType, names));
        results.add(createRenamer(objectType, names));
        results.add(createInliner(objectType, names));
        return results;
    }

    private FieldDeclaration createResolverField() {
        return factory.newFieldDeclaration(
                null,
                new AttributeBuilder(factory)
                    .Private()
                    .Final()
                    .toAttributes(),
                util.t(FlowElementResolver.class),
                factory.newSimpleName(RESOLVER_FIELD_NAME),
                null);
    }

    private MethodDeclaration createRenamer(NamedType objectType, NameGenerator names) {
        assert objectType != null;
        assert names != null;
        SimpleName newName = names.create("newName");
        return factory.newMethodDeclaration(
                new JavadocBuilder(factory)
                    .text("この演算子の名前を設定する。")
                    .param(newName)
                        .text("設定する名前")
                    .returns()
                        .text("この演算子オブジェクト (this)")
                    .exception(util.t(IllegalArgumentException.class))
                        .text("引数に")
                        .code("null")
                        .text("が指定された場合")
                    .toJavadoc(),
                new AttributeBuilder(factory)
                    .Public()
                    .toAttributes(),
                getType(objectType),
                factory.newSimpleName("as"),
                Collections.singletonList(factory.newFormalParameterDeclaration(
                        util.t(String.class),
                        newName)),
                Arrays.asList(new Statement[] {
                        new ExpressionBuilder(factory, factory.newThis())
                            .field(RESOLVER_FIELD_NAME)
                            .method("setName", newName)
                            .toStatement(),
                        new ExpressionBuilder(factory, factory.newThis())
                            .toReturnStatement(),
                }));
    }

    private MethodDeclaration createInliner(NamedType objectType, NameGenerator names) {
        assert objectType != null;
        assert names != null;
        SimpleName optimize = names.create("optimize");
        return factory.newMethodDeclaration(
                new JavadocBuilder(factory)
                    .text("このフロー部品のインライン化状態を設定する。")
                    .param(optimize)
                        .text("trueならば最適化を行い、falseならばステージ構成を保持する")
                    .returns()
                        .text("この演算子オブジェクト (this)")
                    .toJavadoc(),
                new AttributeBuilder(factory)
                    .Public()
                    .toAttributes(),
                getType(objectType),
                factory.newSimpleName("inlined"),
                Collections.singletonList(factory.newFormalParameterDeclaration(
                        util.t(boolean.class),
                        optimize)),
                Arrays.asList(new Statement[] {
                        new ExpressionBuilder(factory, factory.newThis())
                            .field(RESOLVER_FIELD_NAME)
                            .method("getElement")
                            .method("override", factory.newConditionalExpression(
                                    optimize,
                                    new TypeBuilder(factory, util.t(Inline.class))
                                        .field(Inline.FORCE_AGGREGATE.name())
                                        .toExpression(),
                                    new TypeBuilder(factory, util.t(Inline.class))
                                        .field(Inline.KEEP_SEGREGATED.name())
                                        .toExpression()))
                            .toStatement(),
                        new ExpressionBuilder(factory, factory.newThis())
                            .toReturnStatement(),
                }));
    }

    private TypeBodyDeclaration createObjectOutputField(
            OperatorPortDeclaration var,
            NameGenerator names) {
        assert var != null;
        assert names != null;
        return factory.newFieldDeclaration(
                new JavadocBuilder(factory)
                    .inline(var.getDocumentation())
                    .toJavadoc(),
                new AttributeBuilder(factory)
                    .Public()
                    .Final()
                    .toAttributes(),
                util.toSourceType(var.getType().getRepresentation()),
                factory.newSimpleName(names.reserve(var.getName())),
                null);
    }

    private TypeBodyDeclaration createObjectConstructor(
            NamedType objectType,
            NameGenerator names) {
        assert objectType != null;
        List<FormalParameterDeclaration> parameters =
                createParametersForConstructor(names);

        List<Statement> statements = createBodyForConstructor(parameters, names);

        return factory.newConstructorDeclaration(
                null,
                new AttributeBuilder(factory)
                    // Default Package Access
                    .toAttributes(),
                objectType.getName().getLastSegment(),
                parameters,
                statements);
    }

    private List<FormalParameterDeclaration> createParametersForConstructor(
            NameGenerator names) {
        List<FormalParameterDeclaration> parameters = Lists.create();
        for (OperatorPortDeclaration var : flowClass.getInputPorts()) {
            SimpleName name = factory.newSimpleName(names.reserve(var.getName()));
            parameters.add(factory.newFormalParameterDeclaration(
                    util.toSourceType(var.getType().getRepresentation()),
                    name));
        }
        for (OperatorPortDeclaration var : flowClass.getParameters()) {
            SimpleName name = factory.newSimpleName(names.reserve(var.getName()));
            parameters.add(factory.newFormalParameterDeclaration(
                    util.t(var.getType().getRepresentation()),
                    name));
        }
        return parameters;
    }

    private List<Statement> createBodyForConstructor(
            List<FormalParameterDeclaration> parameters,
            NameGenerator names) {
        assert parameters != null;
        List<Statement> statements = Lists.create();
        SimpleName builderName = names.create("builder");
        statements.add(new TypeBuilder(factory, util.t(FlowPartDescription.Builder.class))
            .newObject(factory.newClassLiteral(util.t(flowClass.getElement())))
            .toLocalVariableDeclaration(util.t(FlowPartDescription.Builder.class),
                    builderName));

        Expression[] arguments = new Expression[
                flowClass.getInputPorts().size()
                + flowClass.getOutputPorts().size()
                + flowClass.getParameters().size()
                ];
        for (OperatorPortDeclaration var : flowClass.getInputPorts()) {
            SimpleName name = names.create(var.getName());
            statements.add(new ExpressionBuilder(factory, builderName)
                .method("addInput",
                        util.v(var.getName()),
                        factory.newSimpleName(var.getType().getReference()))
                .toLocalVariableDeclaration(
                        util.toInType(var.getType().getRepresentation()),
                        name));
            arguments[var.getParameterPosition()] = name;
        }
        for (OperatorPortDeclaration var : flowClass.getOutputPorts()) {
            SimpleName name = names.create(var.getName());
            Expression type = toExpression(var);
            assert type != null;
            statements.add(new ExpressionBuilder(factory, builderName)
                .method("addOutput", util.v(var.getName()), type)
                .toLocalVariableDeclaration(
                        util.toOutType(var.getType().getRepresentation()),
                        name));
            arguments[var.getParameterPosition()] = name;
        }
        for (OperatorPortDeclaration var : flowClass.getParameters()) {
            Expression type = toExpression(var);
            SimpleName name = factory.newSimpleName(var.getName());
            statements.add(new ExpressionBuilder(factory, builderName)
                .method("addParameter",
                    util.v(var.getName()),
                    type,
                    name)
                .toStatement());
            arguments[var.getParameterPosition()] = name;
        }
        SimpleName descName = names.create("desc");
        statements.add(new TypeBuilder(factory, getType(util.t(flowClass.getElement())))
            .newObject(arguments)
            .toLocalVariableDeclaration(util.t(FlowDescription.class), descName));

        Expression resolver = new ExpressionBuilder(factory, factory.newThis())
            .field(RESOLVER_FIELD_NAME)
            .toExpression();
        statements.add(new ExpressionBuilder(factory, resolver)
            .assignFrom(new ExpressionBuilder(factory, builderName)
                .method("toResolver", descName)
                .toExpression())
            .toStatement());
        for (OperatorPortDeclaration var : flowClass.getInputPorts()) {
            statements.add(new ExpressionBuilder(factory, resolver)
                .method("resolveInput",
                        util.v(var.getName()),
                        factory.newSimpleName(var.getName()))
                .toStatement());
        }
        for (OperatorPortDeclaration var : flowClass.getOutputPorts()) {
            statements.add(new ExpressionBuilder(factory, factory.newThis())
                .field(var.getName())
                .assignFrom(new ExpressionBuilder(factory, resolver)
                    .method("resolveOutput", util.v(var.getName()))
                    .toExpression())
                .toStatement());
        }
        return statements;
    }

    private Expression toExpression(OperatorPortDeclaration var) throws AssertionError {
        Expression type;
        switch (var.getType().getKind()) {
        case DIRECT:
            type = factory.newClassLiteral(util.t(environment.getErasure(var.getType().getDirect())));
            break;
        case REFERENCE:
            type = factory.newSimpleName(var.getType().getReference());
            break;
        default:
            throw new AssertionError(var.getType().getKind());
        }
        return type;
    }

    private MethodDeclaration createFactoryMethod(NamedType objectType) {
        assert objectType != null;
        JavadocBuilder javadoc = new JavadocBuilder(factory);
        javadoc.inline(flowClass.getDocumentation());
        List<FormalParameterDeclaration> parameters = Lists.create();
        List<Expression> arguments = Lists.create();
        List<Expression> inputMetaData = Lists.create();
        for (OperatorPortDeclaration var : flowClass.getInputPorts()) {
            SimpleName name = factory.newSimpleName(var.getName());
            javadoc.param(name).inline(var.getDocumentation());
            parameters.add(util.toFactoryMethodInput(var, name));
            inputMetaData.add(util.toMetaData(var, arguments.size()));
            arguments.add(name);
        }
        List<Expression> outputMetaData = Lists.create();
        for (OperatorPortDeclaration var : flowClass.getOutputPorts()) {
            outputMetaData.add(util.toMetaData(var, -1));
        }
        List<Expression> parameterMetaData = Lists.create();
        for (OperatorPortDeclaration var : flowClass.getParameters()) {
            SimpleName name = factory.newSimpleName(var.getName());
            javadoc.param(name).inline(var.getDocumentation());
            parameters.add(factory.newFormalParameterDeclaration(
                    util.t(var.getType().getRepresentation()),
                    name));
            parameterMetaData.add(util.toMetaData(var, arguments.size()));
            arguments.add(name);
        }
        Type type = getType(objectType);
        javadoc.returns().text("生成した演算子オブジェクト");
        javadoc.seeType(util.t(flowClass.getElement()));
        return factory.newMethodDeclaration(
                javadoc.toJavadoc(),
                new AttributeBuilder(factory)
                    .annotation(util.t(OperatorInfo.class),
                            "kind", factory.newClassLiteral(util.t(FlowPart.class)),
                            "input", factory.newArrayInitializer(inputMetaData),
                            "output", factory.newArrayInitializer(outputMetaData),
                            "parameter", factory.newArrayInitializer(parameterMetaData))
                    .Public()
                    .toAttributes(),
                util.toTypeParameters(flowClass.getElement()),
                type,
                factory.newSimpleName("create"),
                parameters,
                0,
                Collections.<Type>emptyList(),
                factory.newBlock(
                        new TypeBuilder(factory, type)
                            .newObject(arguments)
                            .toReturnStatement()));
    }

    private Type getType(Type objectType) {
        assert objectType != null;
        assert objectType.getModelKind() != ModelKind.PARAMETERIZED_TYPE;
        Type type;
        if (flowClass.getElement().getTypeParameters().isEmpty()) {
            type = objectType;
        } else {
            type = new TypeBuilder(factory, objectType)
                .parameterize(util.toTypeVariables(flowClass.getElement()))
                .toType();
        }
        return type;
    }

    private SimpleName getClassName() {
        return util.getFactoryName(flowClass.getElement());
    }
}
TOP

Related Classes of com.asakusafw.compiler.operator.flow.FlowFactoryClassGenerator

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.