@Override
  public OperatorMethodDescriptor describe(Context context) {
    Precondition.checkMustNotBeNull(context, "context"); // $NON-NLS-1$

    ExecutableAnalyzer a = new ExecutableAnalyzer(context.environment, context.element);
    if (a.isGeneric()) {
      a.error("単純集計演算子はジェネリックメソッドで宣言できません");
    }
    if (a.isAbstract() == false) {
      a.error("単純集計演算子はabstractで宣言する必要があります");
    }
    TypeConstraint summarized = a.getReturnType();
    if (summarized.isConcreteModel() == false) {
      a.error("単純集計演算子は戻り値にモデルオブジェクト型を指定する必要があります");
    }
    TypeConstraint summarizee = a.getParameterType(0);
    if (summarizee.isModel() == false) {
      a.error(0, "単純集計演算子の最初の引数はモデルオブジェクト型である必要があります");
    }
    for (int i = 1, n = a.countParameters(); i < n; i++) {
      a.error(i, "単純集計演算子にはユーザー引数を利用できません");
    }
    if (a.hasError()) {
      return null;
    }
    if (summarized.isSummarizedModel(summarizee.getType()) == false) {
      a.error("単純集計演算子の戻り値型は最初の引数の集計結果を表す型である必要があります");
      return null;
    }

    ShuffleKey key = summarized.getSummarizeKey();
    Summarize annotation = context.element.getAnnotation(Summarize.class);
    if (annotation == null) {
      a.error("注釈の解釈に失敗しました");
      return null;
    }
    OperatorProcessorUtil.checkPortName(
        a,
        new String[] {
          annotation.summarizedPort(),
        });
    if (a.hasError()) {
      return null;
    }

    Builder builder = new Builder(getTargetAnnotationType(), context);
    builder.addAttribute(FlowBoundary.SHUFFLE);
    builder.addAttribute(a.getObservationCount());
    builder.addAttribute(annotation.partialAggregation());
    builder.setDocumentation(a.getExecutableDocument());
    builder.addInput(
        a.getParameterDocument(0), a.getParameterName(0), a.getParameterType(0).getType(), 0, key);
    builder.addOutput(
        a.getReturnDocument(),
        annotation.summarizedPort(),
        a.getReturnType().getType(),
        null,
        null);
    return builder.toDescriptor();
  }
  @Override
  public OperatorMethodDescriptor describe(Context context) {
    Precondition.checkMustNotBeNull(context, "context"); // $NON-NLS-1$

    ExecutableAnalyzer a = new ExecutableAnalyzer(context.environment, context.element);
    if (a.isAbstract()) {
      a.error("マスタ分岐演算子はabstractで宣言できません");
    }
    List<VariableElement> constants = Collections.emptyList();
    if (a.getReturnType().isEnum() == false) {
      a.error("マスタ分岐演算子は戻り値に列挙型を指定する必要があります");
    } else {
      constants = a.getReturnType().getEnumConstants();
      if (constants.isEmpty()) {
        a.error("マスタ分岐演算子の戻り値は定数が1つ以上宣言された列挙型である必要があります");
      }
    }
    TypeConstraint master = a.getParameterType(0);
    if (master.isModel() == false) {
      a.error(0, "マスタ分岐演算子の1つ目の引数はモデルオブジェクト型である必要があります");
    }
    TypeConstraint transaction = a.getParameterType(1);
    if (transaction.isModel() == false) {
      a.error(1, "マスタ分岐演算子の2つ目の引数はモデルオブジェクト型である必要があります");
    }
    for (int i = 2, n = a.countParameters(); i < n; i++) {
      if (a.getParameterType(i).isBasic() == false) {
        a.error(i, "マスタ分岐演算子の3つ目以降の引数は文字列またはプリミティブ型である必要があります");
      }
    }
    if (a.hasError()) {
      return null;
    }

    ShuffleKey masterKey = a.getParameterKey(0);
    if (masterKey == null) {
      a.error("マスタ分岐演算子の引数には@Key注釈によってグループ化項目を指定する必要があります");
    }
    ShuffleKey transactionKey = a.getParameterKey(1);
    if (transactionKey == null) {
      a.error("マスタ分岐演算子の引数には@Key注釈によってグループ化項目を指定する必要があります");
    }
    ExecutableElement selector = null;
    try {
      selector = MasterKindOperatorAnalyzer.findSelector(context.environment, context);
    } catch (ResolveException e) {
      a.error(e.getMessage());
    }

    if (a.hasError()) {
      return null;
    }

    Builder builder = new Builder(getTargetAnnotationType(), context);
    builder.addAttribute(FlowBoundary.SHUFFLE);
    builder.addAttribute(a.getObservationCount());
    if (selector != null) {
      builder.addOperatorHelper(selector);
    }
    builder.setDocumentation(a.getExecutableDocument());
    builder.addInput(
        a.getParameterDocument(0),
        a.getParameterName(0),
        a.getParameterType(0).getType(),
        0,
        masterKey);
    builder.addInput(
        a.getParameterDocument(1),
        a.getParameterName(1),
        a.getParameterType(1).getType(),
        1,
        transactionKey);
    for (VariableElement var : constants) {
      builder.addOutput(
          a.getDocument(var),
          JavaName.of(var.getSimpleName().toString()).toMemberName(),
          a.getParameterType(1).getType(),
          a.getParameterName(1),
          null);
    }
    for (int i = 2, n = a.countParameters(); i < n; i++) {
      builder.addParameter(
          a.getParameterDocument(i), a.getParameterName(i), a.getParameterType(i).getType(), i);
    }
    return builder.toDescriptor();
  }