private void visitSingleDeclarator(AstNode declaration, AstNode declarator) {

    AstNode docNode;

    AstNode params = declaration.getFirstDescendant(CxxGrammarImpl.parametersAndQualifiers);

    // in case of function declaration,
    // the docNode is set on the declaration node
    if (params != null) {
      docNode = declaration;
    } else {
      AstNode classSpecifier = declaration.getFirstDescendant(CxxGrammarImpl.classSpecifier);

      // if a class is specified on the same declaration,
      // e.g. 'struct {} a;'
      // the documentation node is set on the declarator
      if (classSpecifier != null) {
        docNode = declarator;
      } else {
        docNode = declaration;
      }
    }

    visitDeclarator(declarator, docNode);
  }
  /**
   * Find documentation node, associated documentation, identifier of a <em>public</em> member
   * declarator and visit it as a public API.
   *
   * @param node the <em>public</em> member declarator to visit
   */
  private void visitMemberDeclarator(AstNode node) {

    AstNode container =
        node.getFirstAncestor(CxxGrammarImpl.templateDeclaration, CxxGrammarImpl.classSpecifier);

    if (isOverriddenMethod(node)) {
      // assume that ancestor method is documented
      // and do not count as public API
      return;
    }

    AstNode docNode;

    if (container == null || container.getType() == CxxGrammarImpl.classSpecifier) {
      docNode = node;
    } else {
      docNode = container;
    }

    // look for block documentation
    List<Token> comments = getBlockDocumentation(docNode);

    // documentation may be inlined
    if (comments.isEmpty()) {
      comments = getDeclaratorInlineComment(node);
    }

    // find the identifier to present to concrete visitors
    String id = null;
    AstNode idNode = null;

    // first look for an operator function id
    idNode = node.getFirstDescendant(CxxGrammarImpl.operatorFunctionId);

    if (idNode != null) {
      id = getOperatorId(idNode);
    } else {
      // look for a declarator id
      idNode = node.getFirstDescendant(CxxGrammarImpl.declaratorId);

      if (idNode != null) {
        id = idNode.getTokenValue();
      } else {
        // look for an identifier (e.g in bitfield declaration)
        idNode = node.getFirstDescendant(GenericTokenType.IDENTIFIER);

        if (idNode != null) {
          id = idNode.getTokenValue();
        } else {
          LOG.error("Unsupported declarator at {}", node.getTokenLine());
        }
      }
    }

    if (idNode != null && id != null) {
      visitPublicApi(idNode, id, comments);
    }
  }
  private void visitAliasDeclaration(AstNode aliasDeclNode) {
    if (isPublicApiMember(aliasDeclNode)) {
      logDebug("AliasDeclaration");

      AstNode aliasDeclIdNode = aliasDeclNode.getFirstDescendant(GenericTokenType.IDENTIFIER);

      if (aliasDeclIdNode == null) {
        LOG.error("No identifier found at {}", aliasDeclNode.getTokenLine());
      } else {
        // check if this is a template specification to adjust
        // documentation node
        AstNode template = aliasDeclNode.getFirstAncestor(CxxGrammarImpl.templateDeclaration);
        AstNode docNode = (template != null) ? template : aliasDeclNode;

        // look for block documentation
        List<Token> comments = getBlockDocumentation(docNode);

        // documentation may be inlined
        if (comments.isEmpty()) {
          comments = getDeclaratorInlineComment(aliasDeclNode);
        }

        visitPublicApi(aliasDeclNode, aliasDeclIdNode.getTokenValue(), comments);
      }
    }
  }
  private void visitEnumSpecifier(AstNode enumSpecifierNode) {
    AstNode enumIdNode = enumSpecifierNode.getFirstDescendant(GenericTokenType.IDENTIFIER);

    String enumId = (enumIdNode == null) ? UNNAMED_ENUM_ID : enumIdNode.getTokenValue();

    if (!isPublicApiMember(enumSpecifierNode)) {
      logDebug(enumId + " not in public API");
      return;
    }

    visitPublicApi(enumSpecifierNode, enumId, getBlockDocumentation(enumSpecifierNode));

    // deal with enumeration values
    AstNode enumeratorList = enumSpecifierNode.getFirstDescendant(CxxGrammarImpl.enumeratorList);

    if (enumeratorList != null) {
      for (AstNode definition : enumeratorList.getChildren(CxxGrammarImpl.enumeratorDefinition)) {

        // look for block documentation
        List<Token> comments = getBlockDocumentation(definition);

        // look for inlined doc
        if (comments.isEmpty()) {
          AstNode next = definition.getNextAstNode();

          // inline documentation may be on the next definition token
          // or next curly brace
          if (next != null) {
            // discard COMMA
            if (next.getToken().getType() == CxxPunctuator.COMMA) {
              next = next.getNextAstNode();
            }

            comments = getInlineDocumentation(next.getToken(), definition.getTokenLine());
          }
        }

        visitPublicApi(
            definition,
            definition.getFirstDescendant(GenericTokenType.IDENTIFIER).getTokenValue(),
            comments);
      }
    }
  }
 /**
  * It is responsible for verifying whether the rule is met in the rule base. In the event that the
  * rule is not correct, create message error.
  *
  * @param astNode It is the node that stores all the rules.
  */
 @Override
 public void visitNode(AstNode astNode) {
   try {
     String methodName =
         astNode.getFirstDescendant(ApexGrammarRuleKey.METHOD_IDENTIFIER).getTokenOriginalValue();
     if (!pattern.matcher(methodName).matches()) {
       getContext().createLineViolation(this, MESSAGE, astNode, methodName, format);
     }
   } catch (Exception e) {
     ChecksLogger.logCheckError(this.toString(), "visitNode", e.toString());
   }
 }
  private void visitMemberDeclaration(AstNode memberDeclaration) {

    AstNode declaratorList =
        memberDeclaration.getFirstDescendant(CxxGrammarImpl.memberDeclaratorList);

    if (!isPublicApiMember(memberDeclaration)) {
      // if not part of the API, nothing to measure
      return;
    }

    AstNode subclassSpecifier = memberDeclaration.getFirstDescendant(CxxGrammarImpl.classSpecifier);

    if (subclassSpecifier != null) {
      // sub classes are handled by subscription
      return;
    }

    AstNode functionDef = memberDeclaration.getFirstDescendant(CxxGrammarImpl.functionDefinition);

    if (functionDef != null) {
      // functionDef are handled by subscription
      return;
    }

    if (declaratorList != null) {
      List<AstNode> declarators = declaratorList.getChildren(CxxGrammarImpl.memberDeclarator);

      // if only one declarator, the doc should be placed before the
      // memberDeclaration, or inlined
      if (declarators.size() == 1) {
        visitMemberDeclarator(memberDeclaration);
      } // if several declarators, doc should be placed before each
      // declarator, or inlined
      else {
        for (AstNode declarator : declarators) {
          visitMemberDeclarator(declarator);
        }
      }
    }
  }
  private void visitClassSpecifier(AstNode classSpecifier) {

    // check if this is a template specification to adjust
    // documentation node
    AstNode template = classSpecifier.getFirstAncestor(CxxGrammarImpl.templateDeclaration);
    AstNode docNode = (template != null) ? template : classSpecifier;

    // narrow the identifier search scope to classHead
    AstNode classHead = classSpecifier.getFirstDescendant(CxxGrammarImpl.classHead);

    if (classHead == null) {
      LOG.warn(
          "classSpecifier does not embed a classHead at line " + classSpecifier.getTokenLine());
      return;
    }

    // look for the specifier id
    AstNode id = classHead.getFirstDescendant(CxxGrammarImpl.className);

    AstNode idNode;
    String idName;

    // check if this is an unnamed specifier
    if (id == null) {
      idNode = classSpecifier;
      idName = UNNAMED_CLASSIFIER_ID;
    } else {
      idNode = id;
      idName = id.getTokenValue();
    }

    if (!isPublicApiMember(classSpecifier)) {
      logDebug(idName + " not in public API");
    } else {
      visitPublicApi(idNode, idName, getBlockDocumentation(docNode));
    }
  }
  // XXX may go to a utility class
  private String getOperatorId(AstNode operatorFunctionId) {

    StringBuilder builder = new StringBuilder(operatorFunctionId.getTokenValue());
    AstNode operator = operatorFunctionId.getFirstDescendant(CxxGrammarImpl.operator);

    if (operator != null) {

      AstNode opNode = operator.getFirstChild();
      while (opNode != null) {
        builder.append(opNode.getTokenValue());
        opNode = opNode.getNextSibling();
      }
    }

    return builder.toString();
  }
  private static boolean isPublicApiMember(AstNode node) {
    AstNode access = node;

    // retrieve the accessSpecifier
    do {
      access = access.getPreviousAstNode();
    } while (access != null && access.getType() != CxxGrammarImpl.accessSpecifier);

    if (access != null) {
      return access.getToken().getType() == CxxKeyword.PUBLIC
          || access.getToken().getType() == CxxKeyword.PROTECTED;
    } else {
      AstNode classSpecifier = node.getFirstAncestor(CxxGrammarImpl.classSpecifier);

      if (classSpecifier != null) {

        AstNode enclosingSpecifierNode =
            classSpecifier.getFirstDescendant(CxxKeyword.STRUCT, CxxKeyword.CLASS, CxxKeyword.ENUM);

        if (enclosingSpecifierNode != null) {
          switch ((CxxKeyword) enclosingSpecifierNode.getToken().getType()) {
            case STRUCT:
              // struct members have public access, thus access level
              // is the access level of the enclosing classSpecifier
              return isPublicApiMember(classSpecifier);
            case CLASS:
              // default access in classes is private
              return false;
            default:
              LOG.error(
                  "isPublicApiMember unhandled case: {} at {}",
                  enclosingSpecifierNode.getType(),
                  enclosingSpecifierNode.getTokenLine());
              return false;
          }
        } else {
          LOG.error(
              "isPublicApiMember: failed to get enclosing classSpecifier for node at {}",
              node.getTokenLine());
          return false;
        }
      }

      // global member
      return true;
    }
  }
  private void visitDeclarator(AstNode declarator, AstNode docNode) {
    // look for block documentation
    List<Token> comments = getBlockDocumentation(docNode);

    // documentation may be inlined
    if (comments.isEmpty()) {
      comments = getDeclaratorInlineComment(docNode);
    }

    AstNode declaratorId = declarator.getFirstDescendant(CxxGrammarImpl.declaratorId);

    if (declaratorId == null) {
      LOG.error("null declaratorId: {}", AstXmlPrinter.print(declarator));
    } else {
      visitPublicApi(declaratorId, declaratorId.getTokenValue(), comments);
    }
  }
  private static boolean hasSuperReferenceReturnedExpression(AstNode node) {
    AstNode returnStatement = node.getFirstDescendant(JavaGrammar.RETURN_STATEMENT);
    AstNode expression = returnStatement.getFirstChild(JavaGrammar.EXPRESSION);

    return expression != null && isSuperReference(expression);
  }