Package com.asakusafw.compiler.operator

Source Code of com.asakusafw.compiler.operator.OperatorFactoryClassGenerator

/**
* 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;

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

import javax.annotation.Generated;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;

import com.asakusafw.compiler.common.JavaName;
import com.asakusafw.compiler.common.NameGenerator;
import com.asakusafw.compiler.operator.OperatorProcessor.Context;
import com.asakusafw.utils.collections.Lists;
import com.asakusafw.utils.java.model.syntax.Attribute;
import com.asakusafw.utils.java.model.syntax.ConstructorDeclaration;
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.Javadoc;
import com.asakusafw.utils.java.model.syntax.MethodDeclaration;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
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.Operator;
import com.asakusafw.vocabulary.flow.graph.FlowElementResolver;
import com.asakusafw.vocabulary.flow.graph.OperatorDescription;
import com.asakusafw.vocabulary.flow.graph.ShuffleKey;
import com.asakusafw.vocabulary.operator.OperatorFactory;
import com.asakusafw.vocabulary.operator.OperatorInfo;

/**
* 演算子ファクトリークラスの情報を構築するジェネレータ。
*/
public class OperatorFactoryClassGenerator extends OperatorClassGenerator {

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

    /**
     * インスタンスを生成する。
     * @param environment 環境オブジェクト
     * @param factory DOMを構築するためのファクトリ
     * @param importer インポート宣言を構築するビルダー
     * @param operatorClass 演算子クラスの情報
     * @throws IllegalArgumentException 引数に{@code null}が含まれる場合
     */
    public OperatorFactoryClassGenerator(
            OperatorCompilingEnvironment environment,
            ModelFactory factory,
            ImportBuilder importer,
            OperatorClass operatorClass) {
        super(environment, factory, importer, operatorClass);
    }

    @Override
    public TypeDeclaration generate() {
        // reserves namespaces for the this operator class on ahead
        for (OperatorMethod method : operatorClass.getMethods()) {
            importer.resolvePackageMember(Models.append(
                    factory,
                    getClassName(),
                    getObjectClassName(method.getElement())));
        }
        return super.generate();
    }

    @Override
    protected SimpleName getClassName() {
        return util.getFactoryName(operatorClass.getElement());
    }

    @Override
    protected List<? extends Attribute> getAttribuets() {
        return new AttributeBuilder(factory)
            .annotation(util.t(Generated.class), util.v("{0}:{1}",
                    getClass().getSimpleName(),
                    OperatorCompiler.VERSION))
            .annotation(util.t(OperatorFactory.class),
                    factory.newClassLiteral(util.t(operatorClass.getElement())))
            .Public()
            .toAttributes();
    }

    @Override
    protected Javadoc createJavadoc() {
        return new JavadocBuilder(factory)
            .linkType(util.t(operatorClass.getElement()))
            .text("に関する演算子ファクトリークラス。")
            .seeType(util.t(operatorClass.getElement()))
            .toJavadoc();
    }

    @Override
    protected List<TypeBodyDeclaration> createMembers() {
        NameGenerator names = new NameGenerator(factory);
        List<TypeBodyDeclaration> results = Lists.create();
        for (OperatorMethod method : operatorClass.getMethods()) {
            OperatorProcessor.Context context = new OperatorProcessor.Context(
                    environment,
                    method.getAnnotation(),
                    method.getElement(),
                    importer,
                    names);
            OperatorProcessor processor = method.getProcessor();
            OperatorMethodDescriptor descriptor = processor.describe(context);

            if (descriptor == null) {
                continue;
            }

            TypeDeclaration objectClass = createObjectClass(context, descriptor);
            if (objectClass == null) {
                continue;
            }

            Type objectType = importer.resolvePackageMember(
                    Models.append(factory, getClassName(), objectClass.getName()));
            if (context.element.getTypeParameters().isEmpty() == false) {
                objectType = new TypeBuilder(factory, objectType)
                        .parameterize(util.toTypeVariables(context.element))
                        .toType();
            }
            MethodDeclaration factoryMethod = createFactoryMethod(context, descriptor, objectType);
            if (factoryMethod == null) {
                continue;
            }

            results.add(objectClass);
            results.add(factoryMethod);
        }
        return results;
    }

    private TypeDeclaration createObjectClass(
            Context context,
            OperatorMethodDescriptor descriptor) {
        assert context != null;
        assert descriptor != null;
        SimpleName name = getObjectClassName(context.element);
        NamedType objectType = (NamedType) importer.resolvePackageMember(
                Models.append(factory, getClassName(), name));
        List<TypeParameterDeclaration> typeParameters = util.toTypeParameters(context.element);
        List<TypeBodyDeclaration> members = createObjectMembers(
                context,
                descriptor,
                objectType);
        return factory.newClassDeclaration(
                new JavadocBuilder(factory)
                    .inline(descriptor.getDocumentation())
                    .toJavadoc(),
                new AttributeBuilder(factory)
                    .Public()
                    .Static()
                    .Final()
                    .toAttributes(),
                name,
                typeParameters,
                null,
                Collections.singletonList(util.t(Operator.class)),
                members);
    }

    private SimpleName getObjectClassName(ExecutableElement element) {
        assert element != null;
        return factory.newSimpleName(JavaName.of(element.getSimpleName().toString()).toTypeName());
    }

    private List<TypeBodyDeclaration> createObjectMembers(
            Context context,
            OperatorMethodDescriptor descriptor,
            NamedType objectType) {
        assert context != null;
        assert descriptor != null;
        assert objectType != null;
        List<TypeBodyDeclaration> results = Lists.create();
        results.add(createResolverField(context));
        for (OperatorPortDeclaration var : descriptor.getOutputPorts()) {
            results.add(createObjectOutputField(context, var));
        }
        results.add(createObjectConstructor(context, descriptor, objectType));
        results.add(createRenamer(context, objectType));
        return results;
    }

    private MethodDeclaration createRenamer(Context context, NamedType rawObjectType) {
        assert context != null;
        assert rawObjectType != null;
        Type objectType;
        if (context.element.getTypeParameters().isEmpty()) {
            objectType = rawObjectType;
        } else {
            objectType = new TypeBuilder(factory, rawObjectType)
                .parameterize(util.toTypeVariables(context.element))
                .toType();
        }
        SimpleName newName = context.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(),
                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 FieldDeclaration createResolverField(Context context) {
        assert context != null;
        return factory.newFieldDeclaration(
                null,
                new AttributeBuilder(factory)
                    .Private()
                    .Final()
                    .toAttributes(),
                util.t(FlowElementResolver.class),
                factory.newSimpleName(RESOLVER_FIELD_NAME),
                null);
    }

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

    private ConstructorDeclaration createObjectConstructor(
            Context context,
            OperatorMethodDescriptor descriptor,
            NamedType objectType) {
        assert context != null;
        assert descriptor != null;
        assert objectType != null;

        List<FormalParameterDeclaration> parameters = createParametersForConstructor(context, descriptor);
        List<Statement> statements = createBodyForConstructor(context, descriptor, parameters);
        return factory.newConstructorDeclaration(
                null,
                new AttributeBuilder(factory).toAttributes(),
                objectType.getName().getLastSegment(),
                parameters,
                statements);
    }

    private List<Statement> createBodyForConstructor(
            Context context,
            OperatorMethodDescriptor descriptor,
            List<FormalParameterDeclaration> parameters) {
        assert context != null;
        assert descriptor != null;
        assert parameters != null;
        List<Statement> statements = Lists.create();
        SimpleName builderName = context.names.create("builder");
        statements.add(new TypeBuilder(factory, util.t(OperatorDescription.Builder.class))
            .newObject(factory.newClassLiteral(util.t(descriptor.getAnnotationType())))
            .toLocalVariableDeclaration(
                    util.t(OperatorDescription.Builder.class),
                    builderName));
        statements.add(new ExpressionBuilder(factory, builderName)
            .method("declare",
                    factory.newClassLiteral(util.t(operatorClass.getElement())),
                    factory.newClassLiteral(factory.newNamedType(
                            util.getImplementorName(operatorClass.getElement()))),
                    util.v(descriptor.getName()))
            .toStatement());
        for (VariableElement parameter : context.element.getParameters()) {
            statements.add(new ExpressionBuilder(factory, builderName)
                .method("declareParameter",
                        new TypeBuilder(factory, util.t(environment.getErasure(parameter.asType())))
                            .dotClass()
                            .toExpression())
                .toStatement());
        }

        for (OperatorPortDeclaration var : descriptor.getInputPorts()) {
            ShuffleKey key = var.getShuffleKey();
            List<Expression> arguments = Lists.create();
            arguments.add(util.v(var.getName()));
            arguments.add(factory.newSimpleName(var.getName()));
            if (key != null) {
                arguments.add(toSource(key));
            }
            statements.add(new ExpressionBuilder(factory, builderName)
                .method("addInput", arguments)
                .toStatement());
        }
        for (OperatorPortDeclaration var : descriptor.getOutputPorts()) {
            Expression type = toExpression(var);
            statements.add(new ExpressionBuilder(factory, builderName)
                .method("addOutput", util.v(var.getName()), type)
                .toStatement());
        }
        for (OperatorPortDeclaration var : descriptor.getParameters()) {
            Expression type = toExpression(var);
            statements.add(new ExpressionBuilder(factory, builderName)
                .method("addParameter",
                        util.v(var.getName()),
                        type,
                        factory.newSimpleName(var.getName()))
                .toStatement());
        }
        for (Expression attr : descriptor.getAttributes()) {
            statements.add(new ExpressionBuilder(factory, builderName)
                .method("addAttribute", attr)
                .toStatement());
        }
        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")
                .toExpression())
            .toStatement());
        for (OperatorPortDeclaration var : descriptor.getInputPorts()) {
            statements.add(new ExpressionBuilder(factory, resolver)
                .method("resolveInput",
                        util.v(var.getName()),
                        factory.newSimpleName(var.getName()))
                .toStatement());
        }
        for (OperatorPortDeclaration var : descriptor.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 Expression toSource(ShuffleKey key) {
        assert key != null;
        List<Expression> group = Lists.create();
        for (String property : key.getGroupProperties()) {
            group.add(Models.toLiteral(factory, property));
        }
        List<Expression> order = Lists.create();
        for (ShuffleKey.Order o : key.getOrderings()) {
            order.add(new TypeBuilder(factory, util.t(ShuffleKey.Order.class))
                .newObject(
                        Models.toLiteral(factory, o.getProperty()),
                        new TypeBuilder(factory, util.t(ShuffleKey.Direction.class))
                            .field(o.getDirection().name())
                            .toExpression())
                .toExpression());
        }

        return new TypeBuilder(factory, util.t(ShuffleKey.class))
            .newObject(
                    toList(util.t(String.class), group),
                    toList(util.t(ShuffleKey.Order.class), order))
            .toExpression();
    }

    private Expression toList(Type type, List<Expression> expressions) {
        assert type != null;
        assert expressions != null;
        return new TypeBuilder(factory, util.t(Arrays.class))
            .method("asList", factory.newArrayCreationExpression(
                    factory.newArrayType(type),
                    Collections.<Expression>emptyList(),
                    factory.newArrayInitializer(expressions)))
            .toExpression();
    }

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

    private MethodDeclaration createFactoryMethod(
            Context context,
            OperatorMethodDescriptor descriptor,
            Type objectType) {
        assert context != null;
        assert descriptor != null;
        assert objectType != null;
        JavadocBuilder javadoc = new JavadocBuilder(factory);
        javadoc.inline(descriptor.getDocumentation());
        List<FormalParameterDeclaration> parameters = Lists.create();
        List<Expression> arguments = Lists.create();
        List<Expression> inputMetaData = Lists.create();
        for (OperatorPortDeclaration var : descriptor.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 : descriptor.getOutputPorts()) {
            outputMetaData.add(util.toMetaData(var, -1));
        }
        List<Expression> parameterMetaData = Lists.create();
        for (OperatorPortDeclaration var : descriptor.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);
        }
        javadoc.returns().text("生成した演算子オブジェクト");

        List<Type> rawParameterTypes = Lists.create();
        for (VariableElement var : context.element.getParameters()) {
            rawParameterTypes.add(util.t(environment.getErasure(var.asType())));
        }
        javadoc.seeMethod(
                util.t(operatorClass.getElement()),
                descriptor.getName(),
                rawParameterTypes);

        return factory.newMethodDeclaration(
                javadoc.toJavadoc(),
                new AttributeBuilder(factory)
                    .annotation(util.t(OperatorInfo.class),
                            "kind", factory.newClassLiteral(util.t(descriptor.getAnnotationType())),
                            "input", factory.newArrayInitializer(inputMetaData),
                            "output", factory.newArrayInitializer(outputMetaData),
                            "parameter", factory.newArrayInitializer(parameterMetaData))
                    .Public()
                    .toAttributes(),
                util.toTypeParameters(context.element),
                objectType,
                factory.newSimpleName(JavaName.of(descriptor.getName()).toMemberName()),
                parameters,
                0,
                Collections.<Type>emptyList(),
                factory.newBlock(new TypeBuilder(factory, objectType)
                    .newObject(arguments)
                    .toReturnStatement()));
    }
}
TOP

Related Classes of com.asakusafw.compiler.operator.OperatorFactoryClassGenerator

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.