/**
 * AST visitor to create compare structure.
 *
 * @since 5.0
 */
class CStructureCreatorVisitor extends ASTVisitor {

  private static final String TRANSLATION_UNIT_NAME =
      CUIMessages.CStructureCreatorVisitor_translationUnitName;
  private static final String ANONYMOUS_NAME =
      CoreModelMessages.getString("CElementLabels.anonymous"); // $NON-NLS-1$

  private Stack<DocumentRangeNode> fStack = new Stack<DocumentRangeNode>();
  private IDocument fDocument;
  private String fTranslationUnitFileName;

  /**
   * Create visitor adding nodes to given root.
   *
   * @param root
   */
  public CStructureCreatorVisitor(DocumentRangeNode root) {
    fDocument = root.getDocument();
    fStack.clear();
    fStack.push(root);
    // visitor options
    shouldVisitTranslationUnit = true;
    shouldVisitDeclarations = true;
    shouldVisitEnumerators = true;
    shouldVisitNamespaces = true;
  }

  /*
   * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit)
   */
  @Override
  public int visit(IASTTranslationUnit tu) {
    fTranslationUnitFileName = tu.getFilePath();

    push(ICElement.C_UNIT, TRANSLATION_UNIT_NAME, 0);

    // TODO fix ordering of includes and macros
    // includes
    final IASTPreprocessorIncludeStatement[] includeDirectives = tu.getIncludeDirectives();
    for (int i = 0; i < includeDirectives.length; i++) {
      IASTPreprocessorIncludeStatement includeDirective = includeDirectives[i];
      if (isLocalToFile(includeDirective)) {
        push(
            ICElement.C_INCLUDE,
            new String(includeDirective.getName().toCharArray()),
            getStartOffset(includeDirective));
        pop(getEndOffset(includeDirective));
      }
    }
    // macros
    final IASTPreprocessorMacroDefinition[] macroDefinitions = tu.getMacroDefinitions();
    for (int i = 0; i < macroDefinitions.length; i++) {
      IASTPreprocessorMacroDefinition macroDefinition = macroDefinitions[i];
      if (isLocalToFile(macroDefinition)) {
        push(
            ICElement.C_MACRO,
            new String(macroDefinition.getName().toCharArray()),
            getStartOffset(macroDefinition));
        pop(getEndOffset(macroDefinition));
      }
    }

    return super.visit(tu);
  }

  /**
   * Test whether given AST node is local to the source file and not part of an inclusion.
   *
   * @param node
   * @return <code>true</code> if the node is part of the source file.
   */
  private boolean isLocalToFile(IASTNode node) {
    return fTranslationUnitFileName.equals(node.getContainingFilename());
  }

  /**
   * Compute the start offset of given AST node.
   *
   * @param node
   * @return
   */
  private int getStartOffset(IASTNode node) {
    IASTFileLocation fileLocation = getMinFileLocation(node.getNodeLocations());
    if (fileLocation != null) {
      return fileLocation.getNodeOffset();
    }
    DocumentRangeNode container = getCurrentContainer();
    Object[] children = container.getChildren();
    if (children != null && children.length > 0) {
      Position prevRange = ((DocumentRangeNode) children[children.length - 1]).getRange();
      return prevRange.getOffset() + prevRange.getLength();
    }
    // fallback: use container range start
    Position containerRange = container.getRange();
    return containerRange.getOffset();
  }

  /**
   * Compute the end offset of give AST node.
   *
   * @param node
   * @return
   */
  private int getEndOffset(IASTNode node) {
    IASTFileLocation fileLocation = getMaxFileLocation(node.getNodeLocations());
    if (fileLocation != null) {
      return fileLocation.getNodeOffset() + fileLocation.getNodeLength();
    }
    // fallback: use container range end
    DocumentRangeNode container = getCurrentContainer();
    Position containerRange = container.getRange();
    return containerRange.getOffset() + containerRange.getLength();
  }

  /*
   * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#leave(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit)
   */
  @Override
  public int leave(IASTTranslationUnit tu) {
    super.leave(tu);
    assert getCurrentContainer().getTypeCode() == ICElement.C_UNIT;
    pop(fDocument.getLength());
    return PROCESS_SKIP;
  }

  /*
   * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTDeclaration)
   */
  @Override
  public int visit(IASTDeclaration node) {
    boolean isTemplateDecl = isTemplateDecl(node);
    final int startOffset =
        isTemplateDecl ? getStartOffset(node.getParent()) : getStartOffset(node);
    final int endOffset = getEndOffset(node);
    if (node instanceof IASTFunctionDefinition) {
      IASTFunctionDefinition functionDef = (IASTFunctionDefinition) node;
      final int nodeType;
      if (inClassBody()) {
        nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_METHOD : ICElement.C_METHOD;
      } else {
        nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_FUNCTION : ICElement.C_FUNCTION;
      }
      push(nodeType, getDeclaratorName(functionDef.getDeclarator()), startOffset);
      pop(endOffset);
      return PROCESS_SKIP;
    } else if (node instanceof IASTSimpleDeclaration) {
      IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) node;
      IASTDeclSpecifier declSpec = simpleDecl.getDeclSpecifier();
      if (declSpec instanceof ICPPASTCompositeTypeSpecifier) {
        ICPPASTCompositeTypeSpecifier compositeTypeSpec = (ICPPASTCompositeTypeSpecifier) declSpec;
        final String nodeName = getTypeName(compositeTypeSpec);
        final int nodeType;
        switch (compositeTypeSpec.getKey()) {
          case IASTCompositeTypeSpecifier.k_struct:
            nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_STRUCT : ICElement.C_STRUCT;
            break;
          case IASTCompositeTypeSpecifier.k_union:
            nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_UNION : ICElement.C_UNION;
            break;
          case ICPPASTCompositeTypeSpecifier.k_class:
            nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_CLASS : ICElement.C_CLASS;
            break;
          default:
            assert false : "Unexpected composite type specifier"; // $NON-NLS-1$
            return PROCESS_CONTINUE;
        }
        push(nodeType, nodeName, startOffset);
      } else if (declSpec instanceof IASTEnumerationSpecifier) {
        IASTEnumerationSpecifier enumSpecifier = (IASTEnumerationSpecifier) declSpec;
        push(ICElement.C_ENUMERATION, getEnumerationName(enumSpecifier), startOffset);
      } else {
        IASTDeclarator[] declarators = simpleDecl.getDeclarators();
        for (int i = 0; i < declarators.length; i++) {
          IASTDeclarator declarator = declarators[i];
          int declStartOffset = declarators.length == 1 ? startOffset : getStartOffset(declarator);
          int declEndOffset = declarators.length == 1 ? endOffset : getEndOffset(declarator);
          final String nodeName = getDeclaratorName(declarator);
          if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_typedef) {
            push(ICElement.C_TYPEDEF, nodeName, declStartOffset);
            pop(declEndOffset);
          } else if (declarator instanceof IASTFunctionDeclarator
              && !hasNestedPointerOperators(declarator)) {
            final int nodeType;
            if (inClassBody()) {
              nodeType =
                  isTemplateDecl
                      ? ICElement.C_TEMPLATE_METHOD_DECLARATION
                      : ICElement.C_METHOD_DECLARATION;
            } else {
              nodeType =
                  isTemplateDecl
                      ? ICElement.C_TEMPLATE_FUNCTION_DECLARATION
                      : ICElement.C_FUNCTION_DECLARATION;
            }
            push(nodeType, nodeName, declStartOffset);
            pop(declEndOffset);
          } else if (declarator != null) {
            final int nodeType;
            if (inClassBody()) {
              nodeType = ICElement.C_FIELD;
            } else {
              if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_extern) {
                nodeType = ICElement.C_VARIABLE_DECLARATION;
              } else {
                nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_VARIABLE : ICElement.C_VARIABLE;
              }
            }
            push(nodeType, nodeName, declStartOffset);
            pop(declEndOffset);
          }
        }
      }
    } else if (node instanceof IASTASMDeclaration) {
      // ignored
    } else if (node instanceof ICPPASTVisibilityLabel) {
      // ignored
    } else if (node instanceof ICPPASTNamespaceDefinition) {
      // handled below
    } else if (node instanceof ICPPASTNamespaceAlias) {
      // ignored
    } else if (node instanceof ICPPASTUsingDeclaration) {
      ICPPASTUsingDeclaration usingDecl = (ICPPASTUsingDeclaration) node;
      push(ICElement.C_USING, ASTStringUtil.getQualifiedName(usingDecl.getName()), startOffset);
      pop(endOffset);
    } else if (node instanceof ICPPASTUsingDirective) {
      ICPPASTUsingDirective usingDirective = (ICPPASTUsingDirective) node;
      push(
          ICElement.C_USING,
          ASTStringUtil.getQualifiedName(usingDirective.getQualifiedName()),
          startOffset);
      pop(endOffset);
    } else if (node instanceof ICPPASTLinkageSpecification) {
      // declarations get flattened
    } else if (node instanceof ICPPASTTemplateDeclaration) {
      // handled at child declaration level
    } else if (node instanceof ICPPASTTemplateSpecialization) {
      // ignored
    } else if (node instanceof ICPPASTExplicitTemplateInstantiation) {
      // ignored
    } else if (node instanceof IASTProblemDeclaration) {
      // ignored
    }
    return super.visit(node);
  }

  /*
   * @see org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor#visit(org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition)
   */
  @Override
  public int visit(ICPPASTNamespaceDefinition namespace) {
    push(
        ICElement.C_NAMESPACE,
        ASTStringUtil.getQualifiedName(namespace.getName()),
        getStartOffset(namespace));
    return super.visit(namespace);
  }

  /*
   * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier.IASTEnumerator)
   */
  @Override
  public int visit(IASTEnumerator enumerator) {
    push(
        ICElement.C_ENUMERATOR,
        ASTStringUtil.getQualifiedName(enumerator.getName()),
        getStartOffset(enumerator));
    pop(getEndOffset(enumerator));
    return super.visit(enumerator);
  }

  /*
   * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#leave(org.eclipse.cdt.core.dom.ast.IASTDeclaration)
   */
  @Override
  public int leave(IASTDeclaration node) {
    super.leave(node);
    boolean isTemplateDecl = isTemplateDecl(node);
    final int endOffset = isTemplateDecl ? getEndOffset(node.getParent()) : getEndOffset(node);
    if (node instanceof IASTFunctionDefinition) {
      final int nodeType;
      if (inClassBody()) {
        nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_METHOD : ICElement.C_METHOD;
      } else {
        nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_FUNCTION : ICElement.C_FUNCTION;
      }
      assert getCurrentContainer().getTypeCode() == nodeType;
      pop(endOffset);
    } else if (node instanceof IASTSimpleDeclaration) {
      IASTSimpleDeclaration simpleDecl = (IASTSimpleDeclaration) node;
      IASTDeclSpecifier declSpec = simpleDecl.getDeclSpecifier();
      boolean isCompositeType = false;
      if (declSpec instanceof ICPPASTCompositeTypeSpecifier) {
        isCompositeType = true;
        ICPPASTCompositeTypeSpecifier compositeTypeSpec = (ICPPASTCompositeTypeSpecifier) declSpec;
        final int nodeType;
        switch (compositeTypeSpec.getKey()) {
          case IASTCompositeTypeSpecifier.k_struct:
            nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_STRUCT : ICElement.C_STRUCT;
            break;
          case IASTCompositeTypeSpecifier.k_union:
            nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_UNION : ICElement.C_UNION;
            break;
          case ICPPASTCompositeTypeSpecifier.k_class:
            nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_CLASS : ICElement.C_CLASS;
            break;
          default:
            assert false : "Unexpected composite type specifier"; // $NON-NLS-1$
            return PROCESS_CONTINUE;
        }
        assert getCurrentContainer().getTypeCode() == nodeType;
        pop(isTemplateDecl ? endOffset : getEndOffset(declSpec));
      } else if (declSpec instanceof IASTEnumerationSpecifier) {
        isCompositeType = true;
        assert getCurrentContainer().getTypeCode() == ICElement.C_ENUMERATION;
        pop(getEndOffset(declSpec));
      }
      if (isCompositeType) {
        IASTDeclarator[] declarators = simpleDecl.getDeclarators();
        for (int i = 0; i < declarators.length; i++) {
          IASTDeclarator declarator = declarators[i];
          final String nodeName = getDeclaratorName(declarator);
          final int declStartOffset = getStartOffset(declarator);
          final int declEndOffset = getEndOffset(declarator);
          if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_typedef) {
            push(ICElement.C_TYPEDEF, nodeName, declStartOffset);
            pop(declEndOffset);
          } else if (declarator instanceof IASTFunctionDeclarator
              && !hasNestedPointerOperators(declarator)) {
            final int nodeType;
            if (inClassBody()) {
              nodeType =
                  isTemplateDecl
                      ? ICElement.C_TEMPLATE_METHOD_DECLARATION
                      : ICElement.C_METHOD_DECLARATION;
            } else {
              nodeType =
                  isTemplateDecl
                      ? ICElement.C_TEMPLATE_FUNCTION_DECLARATION
                      : ICElement.C_FUNCTION_DECLARATION;
            }
            push(nodeType, nodeName, declStartOffset);
            pop(declEndOffset);
          } else if (declarator != null) {
            final int nodeType;
            if (inClassBody()) {
              nodeType = ICElement.C_FIELD;
            } else {
              if (declSpec.getStorageClass() == IASTDeclSpecifier.sc_extern) {
                nodeType = ICElement.C_VARIABLE_DECLARATION;
              } else {
                nodeType = isTemplateDecl ? ICElement.C_TEMPLATE_VARIABLE : ICElement.C_VARIABLE;
              }
            }
            push(nodeType, nodeName, declStartOffset);
            pop(declEndOffset);
          }
        }
      }
    } else if (node instanceof IASTASMDeclaration) {
      // ignored
    } else if (node instanceof ICPPASTVisibilityLabel) {
      // ignored
    } else if (node instanceof ICPPASTNamespaceDefinition) {
      // handled below
    } else if (node instanceof ICPPASTNamespaceAlias) {
      // ignored
    } else if (node instanceof ICPPASTUsingDeclaration) {
      // handled in visit
    } else if (node instanceof ICPPASTUsingDirective) {
      // handled in visit
    } else if (node instanceof ICPPASTLinkageSpecification) {
      // declarations get flattened
    } else if (node instanceof ICPPASTTemplateDeclaration) {
      // handled at child declaration level
    } else if (node instanceof ICPPASTTemplateSpecialization) {
      // ignored
    } else if (node instanceof ICPPASTExplicitTemplateInstantiation) {
      // ignored
    } else if (node instanceof IASTProblemDeclaration) {
      // ignored
    }
    return PROCESS_CONTINUE;
  }

  /*
   * @see org.eclipse.cdt.core.dom.ast.cpp.CPPASTVisitor#leave(org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition)
   */
  @Override
  public int leave(ICPPASTNamespaceDefinition namespace) {
    assert getCurrentContainer().getTypeCode() == ICElement.C_NAMESPACE;
    pop(getEndOffset(namespace));
    return super.leave(namespace);
  }

  private DocumentRangeNode getCurrentContainer() {
    return fStack.peek();
  }

  /** Adds a new node with the given type and name to the current container. */
  private void push(int type, String name, int declarationStart) {
    if (name.length() == 0) {
      name = ANONYMOUS_NAME;
    }
    fStack.push(new CNode(getCurrentContainer(), type, name, declarationStart, 0));
  }

  /** Closes the current node by setting its end position and pops it off the stack. */
  private void pop(int declarationEnd) {
    DocumentRangeNode current = getCurrentContainer();
    current.setAppendPosition(declarationEnd);
    current.setLength(declarationEnd - current.getRange().getOffset());
    fStack.pop();
  }

  /** @return <code>true</code> if the current container is class-like. */
  private boolean inClassBody() {
    int typeCode = getCurrentContainer().getTypeCode();
    return typeCode == ICElement.C_CLASS
        || typeCode == ICElement.C_TEMPLATE_CLASS
        || typeCode == ICElement.C_STRUCT
        || typeCode == ICElement.C_TEMPLATE_STRUCT
        || typeCode == ICElement.C_UNION
        || typeCode == ICElement.C_TEMPLATE_UNION;
  }

  /**
   * Test whether the given declaration is a templated declaration.
   *
   * @param node
   * @return <code>true</code> if the declaration is templated.
   */
  private boolean isTemplateDecl(IASTDeclaration node) {
    return node.getParent() instanceof ICPPASTTemplateDeclaration;
  }

  private boolean hasNestedPointerOperators(IASTDeclarator declarator) {
    declarator = declarator.getNestedDeclarator();
    while (declarator != null) {
      if (declarator.getPointerOperators().length > 0) {
        return true;
      }
      declarator = declarator.getNestedDeclarator();
    }
    return false;
  }

  private String getEnumerationName(IASTEnumerationSpecifier enumSpecifier) {
    String nodeName = ASTStringUtil.getQualifiedName(enumSpecifier.getName());
    if (nodeName.length() == 0) {
      nodeName = ANONYMOUS_NAME;
    }
    return nodeName;
  }

  private String getTypeName(IASTCompositeTypeSpecifier compositeTypeSpec) {
    String nodeName = ASTStringUtil.getQualifiedName(compositeTypeSpec.getName());
    if (nodeName.length() == 0) {
      nodeName = ANONYMOUS_NAME;
    }
    return nodeName;
  }

  private String getDeclaratorName(IASTDeclarator node) {
    node = getInnermostDeclarator(node);
    IASTName name = node.getName();
    String nodeName = ASTStringUtil.getQualifiedName(name);
    if (nodeName.length() == 0) {
      nodeName = ANONYMOUS_NAME;
    }
    return nodeName;
  }

  private IASTDeclarator getInnermostDeclarator(IASTDeclarator node) {
    IASTDeclarator nested = node.getNestedDeclarator();
    while (nested != null) {
      node = nested;
      nested = node.getNestedDeclarator();
    }
    return node;
  }

  private static IASTFileLocation getMaxFileLocation(IASTNodeLocation[] locations) {
    if (locations == null || locations.length == 0) {
      return null;
    }
    final IASTNodeLocation nodeLocation = locations[locations.length - 1];
    return nodeLocation.asFileLocation();
  }

  private static IASTFileLocation getMinFileLocation(IASTNodeLocation[] locations) {
    if (locations == null || locations.length == 0) {
      return null;
    }
    final IASTNodeLocation nodeLocation = locations[0];
    return nodeLocation.asFileLocation();
  }
}
Example #2
0
 /** @see CreateElementInTUOperation#getMainTaskName */
 @Override
 public String getMainTaskName() {
   return CoreModelMessages.getString("operation.createFieldProgress"); // $NON-NLS-1$
 }