Package com.asakusafw.compiler.operator

Source Code of com.asakusafw.compiler.operator.OperatorMethodDescriptor$Builder

/**
* 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.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;

import com.asakusafw.compiler.common.Precondition;
import com.asakusafw.compiler.operator.OperatorPortDeclaration.Kind;
import com.asakusafw.compiler.operator.OperatorProcessor.Context;
import com.asakusafw.utils.collections.Lists;
import com.asakusafw.utils.java.jsr269.bridge.Jsr269;
import com.asakusafw.utils.java.model.syntax.DocElement;
import com.asakusafw.utils.java.model.syntax.Expression;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
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.FlowElementAttribute;
import com.asakusafw.vocabulary.flow.graph.OperatorHelper;
import com.asakusafw.vocabulary.flow.graph.ShuffleKey;

/**
* 演算子メソッドの内容を記述する情報。
*/
public class OperatorMethodDescriptor {

    private final Class<? extends Annotation> annotationType;

    private final List<DocElement> documentation;

    private final String name;

    private final List<OperatorPortDeclaration> inputPorts;

    private final List<OperatorPortDeclaration> outputPorts;

    private final List<OperatorPortDeclaration> parameters;

    private final List<Expression> attributes;

    /**
     * インスタンスを生成する。
     * @param annotationType この演算子の種類をあらわす注釈型
     * @param documentation 演算子に関するドキュメンテーション
     * @param name 演算子の名前
     * @param inputPorts 入力ポートの一覧
     * @param outputPorts 出力ポートの一覧
     * @param parameters 入出力に関連のないパラメーターの一覧
     * @param attributes 属性の一覧
     * @throws IllegalArgumentException 引数に{@code null}が含まれる場合
     */
    public OperatorMethodDescriptor(
            Class<? extends Annotation> annotationType,
            List<DocElement> documentation,
            String name,
            List<OperatorPortDeclaration> inputPorts,
            List<OperatorPortDeclaration> outputPorts,
            List<OperatorPortDeclaration> parameters,
            List<Expression> attributes) {
        Precondition.checkMustNotBeNull(annotationType, "annotationType"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(documentation, "documentation"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(name, "name"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(inputPorts, "inputPorts"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(outputPorts, "outputPorts"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(parameters, "parameters"); //$NON-NLS-1$
        this.annotationType = annotationType;
        this.documentation = Lists.freeze(documentation);
        this.name = name;
        this.inputPorts = Lists.freeze(inputPorts);
        this.outputPorts = Lists.freeze(outputPorts);
        this.parameters = Lists.freeze(parameters);
        this.attributes = Lists.freeze(attributes);
    }

    /**
     * この演算子の種類をあらわす注釈型を返す。
     * @return 演算子の種類をあらわす注釈型
     */
    public Class<? extends Annotation> getAnnotationType() {
        return annotationType;
    }

    /**
     * 演算子に関する説明を返す。
     * @return 演算子に関する説明、存在しない場合は{@code null}
     */
    public List<DocElement> getDocumentation() {
        return documentation;
    }

    /**
     * 演算子の名前を返す。
     * @return 演算子の名前
     */
    public String getName() {
        return name;
    }

    /**
     * 入力ポートの一覧を返す。
     * @return 入力ポートの一覧
     */
    public List<OperatorPortDeclaration> getInputPorts() {
        return inputPorts;
    }

    /**
     * 出力ポートの一覧を返す。
     * @return 出力ポートの一覧
     */
    public List<OperatorPortDeclaration> getOutputPorts() {
        return outputPorts;
    }

    /**
     * 入出力に関連のないパラメーターの一覧を返す。
     * @return 入出力に関連のないパラメーターの一覧
     */
    public List<OperatorPortDeclaration> getParameters() {
        return parameters;
    }

    /**
     * この演算子の属性一覧を返す。
     * @return この演算子の属性一覧
     */
    public List<Expression> getAttributes() {
        return attributes;
    }

    /**
     * {@link OperatorMethodDescriptor}を構築するビルダー。
     */
    public static class Builder {

        private final Class<? extends Annotation> annotationType;

        private List<DocElement> operatorDescription;

        private final String name;

        private final List<OperatorPortDeclaration> inputPorts;

        private final List<OperatorPortDeclaration> outputPorts;

        private final List<OperatorPortDeclaration> parameters;

        private final List<Expression> attributes;

        private final Context context;

        /**
         * インスタンスを生成する。
         * @param annotationType 構築対象の演算子の種類を表す注釈型
         * @param context 文脈情報
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public Builder(
                Class<? extends Annotation> annotationType,
                OperatorProcessor.Context context) {
            Precondition.checkMustNotBeNull(annotationType, "annotationType"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(context, "context"); //$NON-NLS-1$
            this.context = context;
            this.annotationType = annotationType;
            this.name = context.element.getSimpleName().toString();
            this.operatorDescription = Lists.create();
            this.inputPorts = Lists.create();
            this.outputPorts = Lists.create();
            this.parameters = Lists.create();
            this.attributes = Lists.create();
        }

        /**
         * Returns an input name which has the specified type variable.
         * @param type target type
         * @return the matched input name, or {@code null} if not exists or is not a projective model
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public String findInput(TypeMirror type) {
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            if (type.getKind() != TypeKind.TYPEVAR) {
                return null;
            }
            Types types = context.environment.getTypeUtils();
            for (OperatorPortDeclaration input : inputPorts) {
                if (types.isSameType(type, input.getType().getRepresentation())) {
                    return input.getName();
                }
            }
            return null;
        }

        /**
         * 演算子の説明を設定する。
         * @param description 設定する説明
         */
        public void setDocumentation(List<? extends DocElement> description) {
            Precondition.checkMustNotBeNull(description, "description"); //$NON-NLS-1$
            this.operatorDescription = Lists.from(description);
        }

        /**
         * 入力を追加する。
         * @param documentation 変数の説明
         * @param varName 変数の名前
         * @param type 変数の型
         * @param position 宣言されたパラメーター上での位置、パラメーターから導出されていない場合は{@code null}
         * @throws IllegalArgumentException 引数に{@code null}が含まれる場合
         */
        public void addInput(
                List<? extends DocElement> documentation,
                String varName,
                TypeMirror type,
                Integer position) {
            addInput(documentation, varName, type, position, null);
        }

        /**
         * 入力を追加する。
         * @param documentation 変数の説明
         * @param varName 変数の名前
         * @param type 変数の型
         * @param position 宣言されたパラメーター上での位置、パラメーターから導出されていない場合は{@code null}
         * @param shuffleKey シャッフル条件
         * @throws IllegalArgumentException 引数に{@code null}が含まれる場合
         */
        public void addInput(
                List<? extends DocElement> documentation,
                String varName,
                TypeMirror type,
                Integer position,
                ShuffleKey shuffleKey) {
            Precondition.checkMustNotBeNull(documentation, "documentation"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(varName, "varName"); //$NON-NLS-1$
            inputPorts.add(new OperatorPortDeclaration(
                    Kind.INPUT,
                    documentation,
                    varName,
                    PortTypeDescription.reference(type, varName),
                    position,
                    shuffleKey));
        }

        /**
         * 出力を追加する。
         * @param documentation 変数の説明
         * @param varName 変数の名前
         * @param type 変数の型
         * @param correspondedInputName この出力と同じ型を持つ変数の名前
         * @param position 宣言されたパラメーター上での位置、パラメーターから導出されていない場合は{@code null}
         * @throws IllegalArgumentException 引数に{@code null}が含まれる場合
         */
        public void addOutput(
                List<? extends DocElement> documentation,
                String varName,
                TypeMirror type,
                String correspondedInputName,
                Integer position) {
            Precondition.checkMustNotBeNull(documentation, "documentation"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(varName, "varName"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            PortTypeDescription typeDesc;
            if (correspondedInputName != null) {
                typeDesc = PortTypeDescription.reference(type, correspondedInputName);
            } else {
                typeDesc = PortTypeDescription.direct(type);
            }
            outputPorts.add(new OperatorPortDeclaration(
                    Kind.OUTPUT,
                    documentation,
                    varName,
                    typeDesc,
                    position,
                    null));
        }

        /**
         * 出力を追加する。
         * @param documentation 変数の説明
         * @param varName 変数の名前
         * @param type 変数の型
         * @param correspondedInputName この出力と同じ型を持つ変数の名前
         * @param position 宣言されたパラメーター上での位置、パラメーターから導出されていない場合は{@code null}
         * @throws IllegalArgumentException 引数に{@code null}が含まれる場合
         */
        public void addOutput(
                String documentation,
                String varName,
                TypeMirror type,
                String correspondedInputName,
                Integer position) {
            Precondition.checkMustNotBeNull(documentation, "documentation"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(varName, "varName"); //$NON-NLS-1$
            List<? extends DocElement> elements = Collections.emptyList();
            if (documentation != null) {
                elements = Collections.singletonList(Models.getModelFactory()
                    .newDocText(documentation));
            }
            addOutput(elements, varName, type, correspondedInputName, position);
        }

        /**
         * 入出力以外のパラメーターを追加する。
         * @param documentation 変数の説明
         * @param varName 変数の名前
         * @param type 変数の型
         * @param position 宣言されたパラメーター上での位置、パラメーターから導出されていない場合は{@code null}
         * @throws IllegalArgumentException 引数に{@code null}が含まれる場合
         */
        public void addParameter(
                List<? extends DocElement> documentation,
                String varName,
                TypeMirror type,
                Integer position) {
            Precondition.checkMustNotBeNull(documentation, "documentation"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(varName, "varName"); //$NON-NLS-1$
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            parameters.add(new OperatorPortDeclaration(
                    Kind.CONSTANT,
                    documentation,
                    varName,
                    PortTypeDescription.direct(type),
                    position,
                    null));
        }

        /**
         * 属性を追加する。
         * @param attribute 追加する属性
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public void addAttribute(Expression attribute) {
            Precondition.checkMustNotBeNull(attribute, "attribute"); //$NON-NLS-1$
            attributes.add(attribute);
        }

        /**
         * 属性を追加する。
         * @param constant 定数表記の{@link FlowElementAttribute}
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public void addAttribute(Enum<? extends FlowElementAttribute> constant) {
            Precondition.checkMustNotBeNull(constant, "constant"); //$NON-NLS-1$
            ModelFactory f = context.environment.getFactory();
            ImportBuilder ib = context.importer;
            Expression attribute = new TypeBuilder(f, ib.toType(constant.getDeclaringClass()))
                .field(constant.name())
                .toExpression();
            addAttribute(attribute);
        }

        /**
         * 属性を追加する。
         * @param helperMethod 補助演算子を表すメソッド
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public void addOperatorHelper(ExecutableElement helperMethod) {
            Precondition.checkMustNotBeNull(helperMethod, "helperMethod"); //$NON-NLS-1$
            ModelFactory f = context.environment.getFactory();
            ImportBuilder ib = context.importer;
            Jsr269 conv = new Jsr269(f);
            List<Expression> parameterTypeLiterals = Lists.create();
            for (VariableElement parameter : helperMethod.getParameters()) {
                TypeMirror type = context.environment.getErasure(parameter.asType());
                parameterTypeLiterals.add(new TypeBuilder(f, ib.resolve(conv.convert(type)))
                    .dotClass()
                    .toExpression());
            }
            Expression attribute = new TypeBuilder(f, ib.toType(OperatorHelper.class))
                .newObject(new Expression[] {
                        // name
                        Models.toLiteral(f, helperMethod.getSimpleName().toString()),
                        // parameter types
                        new TypeBuilder(f, ib.toType(Arrays.class))
                            .method("asList", new TypeBuilder(f, ib.toType(Class.class))
                                .parameterize(f.newWildcard())
                                .array(1)
                                .newArray(f.newArrayInitializer(parameterTypeLiterals))
                                .toExpression())
                            .toExpression()
                }).toExpression();
            addAttribute(attribute);
        }

        /**
         * これまでに構築した情報を元に{@link OperatorMethodDescriptor}を生成して返す。
         * @return 生成した{@link OperatorMethodDescriptor}
         */
        public OperatorMethodDescriptor toDescriptor() {
            return new OperatorMethodDescriptor(
                    annotationType,
                    operatorDescription,
                    name,
                    inputPorts,
                    outputPorts,
                    parameters,
                    attributes);
        }
    }
}
TOP

Related Classes of com.asakusafw.compiler.operator.OperatorMethodDescriptor$Builder

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.