Package com.asakusafw.compiler.operator

Source Code of com.asakusafw.compiler.operator.ExecutableAnalyzer$TypeConstraint

/**
* 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.text.MessageFormat;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

import com.asakusafw.compiler.common.Precondition;
import com.asakusafw.compiler.operator.DataModelMirror.Kind;
import com.asakusafw.runtime.core.Result;
import com.asakusafw.utils.collections.Lists;
import com.asakusafw.utils.collections.Maps;
import com.asakusafw.utils.java.model.syntax.DocBlock;
import com.asakusafw.utils.java.model.syntax.DocElement;
import com.asakusafw.utils.java.model.syntax.Javadoc;
import com.asakusafw.utils.java.model.syntax.ModelFactory;
import com.asakusafw.utils.java.model.syntax.ModelKind;
import com.asakusafw.utils.java.model.syntax.SimpleName;
import com.asakusafw.utils.java.parser.javadoc.JavadocConverter;
import com.asakusafw.utils.java.parser.javadoc.JavadocParseException;
import com.asakusafw.vocabulary.flow.In;
import com.asakusafw.vocabulary.flow.Operator;
import com.asakusafw.vocabulary.flow.Out;
import com.asakusafw.vocabulary.flow.graph.ObservationCount;
import com.asakusafw.vocabulary.flow.graph.ShuffleKey;
import com.asakusafw.vocabulary.model.Joined;
import com.asakusafw.vocabulary.model.Key;
import com.asakusafw.vocabulary.model.Summarized;
import com.asakusafw.vocabulary.operator.Sticky;
import com.asakusafw.vocabulary.operator.Volatile;

/**
* メソッドやコンストラクターの宣言を解析する。
*/
public class ExecutableAnalyzer {

    final OperatorCompilingEnvironment environment;

    final ExecutableElement executable;

    final Javadoc documentation;

    private boolean sawError;

    /**
     * インスタンスを生成する。
     * @param environment 注釈処理の環境
     * @param executable メソッドやコンストラクタの宣言
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public ExecutableAnalyzer(
            OperatorCompilingEnvironment environment,
            ExecutableElement executable) {
        Precondition.checkMustNotBeNull(environment, "environment"); //$NON-NLS-1$
        Precondition.checkMustNotBeNull(executable, "executable"); //$NON-NLS-1$
        this.environment = environment;
        this.executable = executable;
        this.documentation = getJavadoc(environment, executable);
        this.sawError = false;
    }

    /**
     * この要素に対してエラーを表示する。
     * @param message 表示するメッセージ、引数を指定した場合はメッセージのフォーマット
     * @param arguments メッセージの引数
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public void error(String message, Object...arguments) {
        Precondition.checkMustNotBeNull(message, "message"); //$NON-NLS-1$
        environment.getMessager().printMessage(
                Diagnostic.Kind.ERROR,
                format(message, arguments),
                executable);
        sawError = true;
    }

    /**
     * パラメーターに対してエラーを表示する。
     * @param parameterIndex パラメーターの位置 (0起算)
     * @param message 表示するメッセージ、引数を指定した場合はメッセージのフォーマット
     * @param arguments メッセージの引数
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public void error(int parameterIndex, String message, Object...arguments) {
        Precondition.checkMustNotBeNull(message, "message"); //$NON-NLS-1$
        if (parameterIndex < 0 || parameterIndex >= executable.getParameters().size()) {
            error(format(message, arguments));
            return;
        }
        environment.getMessager().printMessage(
                Diagnostic.Kind.ERROR,
                format(message, arguments),
                executable.getParameters().get(parameterIndex));
        sawError = true;
    }

    boolean typeEqual(TypeMirror a, TypeMirror b) {
        Types types = environment.getTypeUtils();
        return types.isSameType(a, b);
    }

    boolean typeDeclEqual(TypeMirror a, TypeMirror b) {
        if (a.getKind() != TypeKind.DECLARED) {
            return false;
        }
        if (b.getKind() != TypeKind.DECLARED) {
            return false;
        }
        Types types = environment.getTypeUtils();
        if (types.isSameType(a, b)) {
            return true;
        }
        // EclipseJDTのバグでerasureは正しく計算されない模様
        DeclaredType at = (DeclaredType) a;
        DeclaredType bt = (DeclaredType) b;
        return at.asElement().equals(bt.asElement());
    }

    private String format(String message, Object... arguments) {
        if (arguments == null || arguments.length == 0) {
            return message;
        } else {
            return MessageFormat.format(message, arguments);
        }
    }

    /**
     * {@code error()}メソッドが利用された場合に{@code true}を返す。
     * @return {@code error()}メソッドが利用された場合に{@code true}
     */
    public boolean hasError() {
        return sawError;
    }

    /**
     * この要素が{@code abstract}として宣言されている場合に{@code true}を返す。
     * @return {@code abstract}として宣言されている場合に{@code true}
     */
    public boolean isAbstract() {
        return executable.getModifiers().contains(Modifier.ABSTRACT);
    }

    /**
     * Returns {@code true} iff this element is declared as a generic method.
     * @return {@code true} iff this element is declared as a generic method
     */
    public boolean isGeneric() {
        return executable.getTypeParameters().isEmpty() == false;
    }

    /**
     * この要素の観測回数に関する属性を計算する。
     * @param defaults この要素が本質的に有する観測回数に関する制約
     * @return 観測回数に関する属性
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public ObservationCount getObservationCount(ObservationCount... defaults) {
        Precondition.checkMustNotBeNull(defaults, "defaults"); //$NON-NLS-1$
        ObservationCount current = ObservationCount.DONT_CARE;
        for (ObservationCount oc : defaults) {
            current = current.and(oc);
        }
        if (current.atLeastOnce == false) {
            if (executable.getAnnotation(Sticky.class) != null) {
                current = current.and(ObservationCount.AT_LEAST_ONCE);
            }
        }
        if (current.atMostOnce == false) {
            if (executable.getAnnotation(Volatile.class) != null) {
                current = current.and(ObservationCount.AT_MOST_ONCE);
            }
        }
        return current;
    }

    /**
     * 指定の要素のドキュメンテーションを返す。
     * @param element 対象の要素
     * @return ドキュメンテーション、存在しない場合は空のリスト
     * @throws IllegalArgumentException 引数に{@code null}が指定された場合
     */
    public List<? extends DocElement> getDocument(Element element) {
        Precondition.checkMustNotBeNull(element, "element"); //$NON-NLS-1$
        Javadoc doc = getJavadoc(environment, element);
        return getAbstractBlock(doc);
    }

    private static Javadoc getJavadoc(
            OperatorCompilingEnvironment environment,
            Element element) {
        assert environment != null;
        assert element != null;
        ModelFactory f = environment.getFactory();
        String comment = environment.getElementUtils().getDocComment(element);
        if (comment == null) {
            return f.newJavadoc(Collections.<DocBlock>emptyList());
        }
        if (comment.startsWith("/**") == false) {
            comment = "/**" + comment;
        }
        if (comment.endsWith("*/") == false) {
            comment = comment + "*/";
        }

        try {
            return new JavadocConverter(f).convert(comment, 0);
        } catch (JavadocParseException e) {
            environment.getMessager().printMessage(Diagnostic.Kind.ERROR,
                    e.getMessage(),
                    element);
            return f.newJavadoc(Collections.<DocBlock>emptyList());
        }
    }

    private static List<? extends DocElement> getAbstractBlock(Javadoc doc) {
        assert doc != null;
        List<? extends DocBlock> blocks = doc.getBlocks();
        if (blocks.isEmpty()) {
            return Collections.emptyList();
        }
        DocBlock first = blocks.get(0);
        if (first.getTag().equals("") == false) {
            return Collections.emptyList();
        }
        return first.getElements();
    }

    /**
     * この要素に宣言されたパラメーターの個数を返す。
     * @return この要素に宣言されたパラメーターの個数
     */
    public int countParameters() {
        return executable.getParameters().size();
    }

    /**
     * 戻り値の型に対する制約オブジェクトを返す。
     * @return 戻り値の型に対する制約オブジェクト
     */
    public TypeConstraint getReturnType() {
        return new TypeConstraint(executable.getReturnType());
    }

    /**
     * 指定の引数の型に対する制約オブジェクトを返す。
     * @param index 対象の位置 (0起算)
     * @return 指定の引数の型に対する制約オブジェクト
     */
    public TypeConstraint getParameterType(int index) {
        List<? extends VariableElement> parameters = executable.getParameters();
        if (index >= parameters.size()) {
            return new TypeConstraint(environment.getTypeUtils().getNoType(TypeKind.NONE));
        }
        return new TypeConstraint(parameters.get(index).asType());
    }

    /**
     * 指定の位置の引数名を返す。
     * @param index 対象の位置 (0起算)
     * @return 指定の位置の引数名
     */
    public String getParameterName(int index) {
        List<? extends VariableElement> parameters = executable.getParameters();
        String name = parameters.get(index).getSimpleName().toString();
        return name;
    }

    /**
     * 指定の位置の引数に付与されたキーを返す。
     * @param index 対象の位置 (0起算)
     * @return 指定の位置の引数に付与されたキー
     */
    public ShuffleKey getParameterKey(int index) {
        VariableElement parameter = executable.getParameters().get(index);
        TypeConstraint type = getParameterType(index);

        // TODO 外側からしっかりと型を指定させる?
        TypeConstraint arg = type.getTypeArgument();
        if (arg.exists()) {
            type = arg;
        }
        DataModelMirror model = environment.loadDataModel(type.getType());
        if (model == null) {
            return null;
        }
        return toShuffleKey(index, model, findAnnotation(parameter, environment.getDeclaredType(Key.class)));
    }

    /**
     * この要素に対するドキュメント(概要のみ)を返す。
     * @return この要素に対するドキュメント、存在しない場合は空のリスト
     */
    public List<? extends DocElement> getExecutableDocument() {
        Javadoc doc = documentation;
        return getAbstractBlock(doc);
    }

    /**
     * この要素の引数に対するドキュメントを返す。
     * @param index 対象の位置 (0起算)
     * @return この要素の引数に対するドキュメント、存在しない場合は空のリスト
     */
    public List<? extends DocElement> getParameterDocument(int index) {
        String name = getParameterName(index);
        for (DocBlock block : documentation.getBlocks()) {
            if (block.getTag().equals("@param") == false) {
                continue;
            }
            List<? extends DocElement> elements = block.getElements();
            if (elements.isEmpty()) {
                continue;
            }
            DocElement first = elements.get(0);
            if (first.getModelKind() != ModelKind.SIMPLE_NAME) {
                continue;
            }
            if (name.equals(((SimpleName) first).getToken()) == false) {
                continue;
            }
            return elements.subList(1, elements.size());
        }
        return Collections.emptyList();
    }

    /**
     * この要素の戻り値に対するドキュメントを返す。
     * @return この要素の戻り値に対するドキュメント、存在しない場合は空のリスト
     */
    public List<? extends DocElement> getReturnDocument() {
        for (DocBlock block : documentation.getBlocks()) {
            if (block.getTag().equals("@return") == false
                    && block.getTag().equals("@returns") == false) {
                continue;
            }
            return block.getElements();
        }
        return Collections.emptyList();
    }

    ShuffleKey toShuffleKey(
            int position,
            DataModelMirror model,
            AnnotationMirror annotation) {
        if (annotation == null) {
            return null;
        }
        ShuffleKey key = toUncheckedShuffleKey(position, annotation);
        if (key == null) {
            return null;
        }
        checkShuffleKey(position, model, key);
        return key;
    }

    ShuffleKey toUncheckedShuffleKey(
            int position,
            AnnotationMirror annotation) {
        assert annotation != null;
        Map<String, AnnotationValue> values = getValues(annotation);
        List<String> group = toStringList(values.get("group"));
        List<String> order = toStringList(values.get("order"));
        if (group == null) {
            error(position, "@Keyにgroupが指定されていません");
            return null;
        }
        if (order == null) {
            order = Collections.emptyList();
        }
        List<ShuffleKey.Order> formedOrder = Lists.create();
        for (String orderString : order) {
            ShuffleKey.Order o = ShuffleKey.Order.parse(orderString);
            if (o == null) {
                error(position, "@Keyのorder \"{0}\" を正しく解析できません", orderString);
            } else {
                formedOrder.add(o);
            }
        }
        return new ShuffleKey(group, formedOrder);
    }

    private void checkShuffleKey(int position, DataModelMirror model, ShuffleKey key) {
        assert model != null;
        assert key != null;
        for (String name : key.getGroupProperties()) {
            if (model.findProperty(name) == null) {
                error(position, "@Keyのgroupに指定されたプロパティ\"{0}\"が見つかりません({1})",
                        name,
                        model);
            }
        }
        for (ShuffleKey.Order order : key.getOrderings()) {
            if (model.findProperty(order.getProperty()) == null) {
                error(position, "@Keyのorderに指定されたプロパティ\"{0}\"が見つかりません({1})",
                        order.getProperty(),
                        model);
            }
        }
    }

    List<String> toStringList(AnnotationValue value) {
        if (value == null) {
            return null;
        }
        Object object = value.getValue();
        if (object instanceof String) {
            return Collections.singletonList((String) object);
        }
        if ((object instanceof List<?>) == false) {
            return null;
        }
        List<?> list = (List<?>) object;
        List<String> results = Lists.create();
        for (Object element : list) {
            Object elementValue = ((AnnotationValue) element).getValue();
            if ((elementValue instanceof String) == false) {
                return null;
            }
            results.add((String) elementValue);
        }
        return results;
    }

    AnnotationMirror findAnnotation(
            Element elem,
            DeclaredType annotationType) {
        assert annotationType != null;
        if (elem == null) {
            return null;
        }
        for (AnnotationMirror annotation : elem.getAnnotationMirrors()) {
            DeclaredType aType = annotation.getAnnotationType();
            if (typeEqual(aType, annotationType)) {
                return annotation;
            }
        }
        return null;
    }

    static <T> T getValue(
            Class<T> valueType,
            Map<String, AnnotationValue> valueMap,
            String name) {
        assert valueType != null;
        assert valueMap != null;
        assert name != null;
        AnnotationValue value = valueMap.get(name);
        if (value == null) {
            return null;
        }
        Object content = value.getValue();
        if (valueType.isInstance(content) == false) {
            return null;
        }
        return valueType.cast(content);
    }

    @SuppressWarnings("unchecked")
    static List<? extends AnnotationValue> getList(
            Map<String, AnnotationValue> valueMap,
            String name) {
        assert valueMap != null;
        assert name != null;
        AnnotationValue value = valueMap.get(name);
        if (value == null) {
            return null;
        }
        Object content = value.getValue();
        if ((content instanceof List<?>) == false) {
            return null;
        }
        return (List<? extends AnnotationValue>) content;
    }

    static TypeMirror getReduceTermType(AnnotationMirror annotation) {
        assert annotation != null;
        Map<String, AnnotationValue> values = getValues(annotation);
        return getValue(TypeMirror.class, values, "source");
    }

    static AnnotationMirror getReduceTermKey(AnnotationMirror annotation) {
        assert annotation != null;
        Map<String, AnnotationValue> values = getValues(annotation);
        return getValue(AnnotationMirror.class, values, "shuffle");
    }

    static Map<String, AnnotationValue> getValues(
            AnnotationMirror annotation) {
        assert annotation != null;
        Map<String, AnnotationValue> results = Maps.create();
        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry
                : annotation.getElementValues().entrySet()) {
            ExecutableElement key = entry.getKey();
            AnnotationValue value = entry.getValue();
            results.put(key.getSimpleName().toString(), value);
        }
        return results;
    }

    /**
     * 型に関する制約。
     */
    public class TypeConstraint {

        private final TypeMirror type;

        private final Element element;

        /**
         * インスタンスを生成する。
         * @param type 対象の型
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        TypeConstraint(TypeMirror type) {
            Precondition.checkMustNotBeNull(type, "type"); //$NON-NLS-1$
            this.type = type;
            this.element = environment.getTypeUtils().asElement(type);
        }

        /**
         * このオブジェクトが制約の対象としている型を返す。
         * @return 対象の型
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public TypeMirror getType() {
            return type;
        }

        /**
         * この型が存在する場合のみ{@code true}を返す。
         * @return この型が存在する場合のみ{@code true}
         */
        public boolean exists() {
            return type.getKind() != TypeKind.NONE;
        }

        /**
         * この型が{@code void}を表現する場合のみ{@code true}を返す。
         * @return {@code void}を表現する場合のみ{@code true}
         */
        public boolean isVoid() {
            return type.getKind() == TypeKind.VOID;
        }

        /**
         * この型が型変数を表現する場合のみ{@code true}を返す。
         * @return この型が型変数を表現する場合のみ{@code true}
         */
        public boolean isTypeVariable() {
            return type.getKind() == TypeKind.TYPEVAR;
        }

        /**
         * この型が任意の列挙型を表現する場合のみ{@code true}を返す。
         * @return 任意の列挙型を表現する場合のみ{@code true}
         */
        public boolean isEnum() {
            if (element == null) {
                return false;
            }
            if (element.getKind() == ElementKind.ENUM && element.getModifiers().contains(Modifier.PUBLIC)) {
                return true;
            }
            return false;
        }

        /**
         * この型が型要素で表現される場合にそれを返す。
         * @return この型に対する型要素、存在しない場合は{@code null}
         */
        public TypeElement getTypeElement() {
            if (element instanceof TypeElement) {
                return (TypeElement) element;
            }
            return null;
        }

        /**
         * この型が列挙型である場合に、それらの定数一覧の情報を返す。
         * @return 定数一覧の情報
         * @throws IllegalStateException この型が列挙型を表さない場合
         */
        public List<VariableElement> getEnumConstants() {
            if (isEnum() == false) {
                throw new IllegalStateException();
            }
            TypeElement decl = (TypeElement) element;
            List<VariableElement> results = Lists.create();
            for (Element member : decl.getEnclosedElements()) {
                if (isEnumConstant(member)) {
                    results.add((VariableElement) member);
                }
            }
            return results;
        }

        private boolean isEnumConstant(Element member) {
            if (member.getKind() != ElementKind.ENUM_CONSTANT) {
                return false;
            }
            // for Eclipse APT bug
            Set<Modifier> modifiers = member.getModifiers();
            if (modifiers.contains(Modifier.PUBLIC) == false
                    || modifiers.contains(Modifier.STATIC) == false
                    || modifiers.contains(Modifier.FINAL) == false) {
                return false;
            }
            VariableElement variable = (VariableElement) member;
            TypeMirror constantType = variable.asType();
            if (constantType.getKind() != TypeKind.DECLARED) {
                return false;
            }
            Types types = environment.getTypeUtils();
            return types.isSameType(constantType, type);
        }

        /**
         * この型が演算子オブジェクトを表現する場合のみ{@code true}を返す。
         * @return この型が演算子オブジェクトを表現する場合のみ{@code true}
         */
        public boolean isOperator() {
            return environment.getTypeUtils().isSubtype(type, environment.getDeclaredType(Operator.class));
        }

        /**
         * この型がモデルを表現する場合のみ{@code true}を返す。
         * @return この型がモデルを表現する場合のみ{@code true}
         */
        public boolean isModel() {
            DataModelMirror model = environment.loadDataModel(type);
            return model != null;
        }

        /**
         * この型が射影でないモデルを表現する場合のみ{@code true}を返す。
         * @return この型が射影でないモデルを表現する場合のみ{@code true}
         */
        public boolean isConcreteModel() {
            DataModelMirror model = environment.loadDataModel(type);
            return model != null && model.getKind() == Kind.CONCRETE;
        }

        /**
         * この型が射影モデルを表現する場合のみ{@code true}を返す。
         * @return この型が射影モデルを表現する場合のみ{@code true}
         */
        public boolean isProjectiveModel() {
            DataModelMirror model = environment.loadDataModel(type);
            return model != null && model.getKind() == Kind.PARTIAL;
        }

        /**
         * この型が結合モデルを表現する場合のみ{@code true}を返す。
         * @param a 結合する型
         * @param b 結合する型
         * @return この型が結合モデルを表現する場合のみ{@code true}
         */
        public boolean isJoinedModel(TypeMirror a, TypeMirror b) {
            AnnotationMirror annotation = findAnnotation(element, environment.getDeclaredType(Joined.class));
            if (annotation == null) {
                return false;
            }
            Map<String, AnnotationValue> values = getValues(annotation);
            List<? extends AnnotationValue> terms = getList(values, "terms");
            if (terms == null
                    || terms.size() != 2
                    || (terms.get(0).getValue() instanceof AnnotationMirror) == false
                    || (terms.get(1).getValue() instanceof AnnotationMirror) == false) {
                return false;
            }
            AnnotationMirror from = (AnnotationMirror) terms.get(0).getValue();
            AnnotationMirror join = (AnnotationMirror) terms.get(1).getValue();
            TypeMirror fromType = getReduceTermType(from);
            TypeMirror joinType = getReduceTermType(join);
            if (fromType == null || joinType == null) {
                return false;
            }
            if (environment.getTypeUtils().isSameType(a, fromType)) {
                return environment.getTypeUtils().isSameType(b, joinType);
            }
            if (environment.getTypeUtils().isSameType(b, fromType)) {
                return environment.getTypeUtils().isSameType(a, joinType);
            }
            return false;
        }

        /**
         * この型が結合モデルを表現し、かつ指定の型が左結合項に出現する場合のみ{@code true}を返す。
         * @param target 対象の型
         * @return この型が結合モデルを表現する場合のみ{@code true}
         */
        public boolean isJoinFrom(TypeMirror target) {
            Precondition.checkMustNotBeNull(target, "target"); //$NON-NLS-1$
            AnnotationMirror annotation = findAnnotation(element, environment.getDeclaredType(Joined.class));
            if (annotation == null) {
                return false;
            }
            Map<String, AnnotationValue> values = getValues(annotation);
            List<? extends AnnotationValue> terms = getList(values, "terms");
            if (terms == null
                    || terms.isEmpty()
                    || (terms.get(0).getValue() instanceof AnnotationMirror) == false) {
                return false;
            }
            AnnotationMirror from = (AnnotationMirror) terms.get(0).getValue();
            TypeMirror fromType = getReduceTermType(from);
            if (fromType == null) {
                return false;
            }
            return typeEqual(fromType, target);
        }

        /**
         * この型が結合をモデルを表す場合に、指定の型に関する結合キーを返す。
         * @param target 対象の型
         * @return 結合キー
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public ShuffleKey getJoinKey(TypeMirror target) {
            Precondition.checkMustNotBeNull(target, "target"); //$NON-NLS-1$
            DataModelMirror model = environment.loadDataModel(target);
            AnnotationMirror annotation = findAnnotation(element, environment.getDeclaredType(Joined.class));
            if (model == null || annotation == null) {
                throw new IllegalArgumentException();
            }
            Map<String, AnnotationValue> values = getValues(annotation);
            List<? extends AnnotationValue> terms = getList(values, "terms");
            if (terms == null) {
                throw new IllegalArgumentException();
            }
            for (AnnotationValue value : terms) {
                if ((value.getValue() instanceof AnnotationMirror) == false) {
                    continue;
                }
                AnnotationMirror term = (AnnotationMirror) value.getValue();
                if (typeEqual(target, getReduceTermType(term))) {
                    AnnotationMirror shuffle = getReduceTermKey(term);
                    ShuffleKey key = toShuffleKey(-1, model, shuffle);
                    return key;
                }
            }
            throw new IllegalArgumentException();
        }

        /**
         * この型が集計モデルを表現する場合のみ{@code true}を返す。
         * @param target 集計する型
         * @return この型が集計モデルを表現する場合のみ{@code true}
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public boolean isSummarizedModel(TypeMirror target) {
            Precondition.checkMustNotBeNull(target, "target"); //$NON-NLS-1$
            AnnotationMirror annotation = findAnnotation(element, environment.getDeclaredType(Summarized.class));
            if (annotation == null) {
                return false;
            }
            Map<String, AnnotationValue> values = getValues(annotation);
            AnnotationMirror from = getValue(AnnotationMirror.class, values, "term");
            if (from == null) {
                return false;
            }
            TypeMirror fromType = getReduceTermType(from);
            if (fromType == null) {
                return false;
            }
            return typeEqual(fromType, target);
        }

        /**
         * この型が集約をモデルを表す場合に、指定の型に関する結合キーを返す。
         * @return 集約キー
         * @throws IllegalArgumentException 引数に{@code null}が指定された場合
         */
        public ShuffleKey getSummarizeKey() {
            AnnotationMirror annotation = findAnnotation(element, environment.getDeclaredType(Summarized.class));
            if (annotation == null) {
                throw new IllegalArgumentException();
            }
            Map<String, AnnotationValue> values = getValues(annotation);
            AnnotationMirror reduce = getValue(AnnotationMirror.class, values, "term");
            if (reduce == null) {
                throw new IllegalArgumentException();
            }
            TypeMirror shuffleType = getReduceTermType(reduce);
            DataModelMirror model = environment.loadDataModel(shuffleType);
            AnnotationMirror shuffleKey = getReduceTermKey(reduce);
            if (model != null && shuffleKey != null) {
                return toShuffleKey(-1, model, shuffleKey);
            }
            throw new IllegalArgumentException();
        }

        /**
         * この型が論理値を表現する場合のみ{@code true}を返す。
         * @return 論理値を表現する場合のみ{@code true}
         */
        public boolean isBoolean() {
            return type.getKind() == TypeKind.BOOLEAN;
        }

        /**
         * この型が文字列を表現する場合のみ{@code true}を返す。
         * @return 文字列を表現する場合のみ{@code true}
         */
        public boolean isString() {
            return typeEqual(type, environment.getDeclaredType(String.class));
        }

        /**
         * この型がリストを表現する場合のみ{@code true}を返す。
         * @return リストを表現する場合のみ{@code true}
         * @see #getTypeArgument()
         */
        public boolean isList() {
            return typeDeclEqual(type, environment.getDeclaredType(List.class));
        }

        /**
         * この型が結果を表現する場合のみ{@code true}を返す。
         * @return 結果を表現する場合のみ{@code true}
         * @see #getTypeArgument()
         */
        public boolean isResult() {
            return typeDeclEqual(type, environment.getDeclaredType(Result.class));
        }

        /**
         * この型がフローへの入力を表現する場合のみ{@code true}を返す。
         * @return フローへの入力を表現する場合のみ{@code true}
         * @see #getTypeArgument()
         */
        public boolean isIn() {
            return typeDeclEqual(type, environment.getDeclaredType(In.class));
        }

        /**
         * この型がフローへの出力を表現する場合のみ{@code true}を返す。
         * @return フローへの出力を表現する場合のみ{@code true}
         * @see #getTypeArgument()
         */
        public boolean isOut() {
            return typeDeclEqual(type, environment.getDeclaredType(Out.class));
        }

        /**
         * この型が文字列またはプリミティブ型を表現するする場合のみ{@code true}を返す。
         * @return 文字列またはプリミティブ型を表現する場合のみ{@code true}
         * @see #getTypeArgument()
         */
        public boolean isBasic() {
            if (type.getKind().isPrimitive() || typeEqual(type, environment.getDeclaredType(String.class))) {
                return true;
            }
            return false;
        }

        /**
         * この型の最初の型引数に対する制約オブジェクトを返す。
         * @return この型の最初の型引数に対する制約オブジェクト
         */
        public TypeConstraint getTypeArgument() {
            if (type.getKind() != TypeKind.DECLARED) {
                return new TypeConstraint(environment.getTypeUtils().getNoType(TypeKind.NONE));
            }
            DeclaredType declared = (DeclaredType) type;
            List<? extends TypeMirror> arguments = declared.getTypeArguments();
            if (arguments.isEmpty()) {
                return new TypeConstraint(environment.getTypeUtils().getNoType(TypeKind.NONE));
            }
            return new TypeConstraint(arguments.get(0));
        }
    }
}
TOP

Related Classes of com.asakusafw.compiler.operator.ExecutableAnalyzer$TypeConstraint

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.