/** * Converts ParseTree (that is generated by ANTLRv4) to DetailNode tree. * * @param parseTreeNode root node of ParseTree * @return root of DetailNode tree */ private DetailNode convertParseTreeToDetailNode(ParseTree parseTreeNode) { final JavadocNodeImpl rootJavadocNode = createRootJavadocNode(parseTreeNode); JavadocNodeImpl currentJavadocParent = rootJavadocNode; ParseTree parseTreeParent = parseTreeNode; while (currentJavadocParent != null) { // remove unnecessary children tokens if (currentJavadocParent.getType() == JavadocTokenTypes.TEXT) { currentJavadocParent.setChildren((DetailNode[]) JavadocNodeImpl.EMPTY_DETAIL_NODE_ARRAY); } final JavadocNodeImpl[] children = (JavadocNodeImpl[]) currentJavadocParent.getChildren(); insertChildrenNodes(children, parseTreeParent); if (children.length > 0) { currentJavadocParent = children[0]; parseTreeParent = parseTreeParent.getChild(0); } else { JavadocNodeImpl nextJavadocSibling = (JavadocNodeImpl) JavadocUtils.getNextSibling(currentJavadocParent); ParseTree nextParseTreeSibling = getNextSibling(parseTreeParent); if (nextJavadocSibling == null) { JavadocNodeImpl tempJavadocParent = (JavadocNodeImpl) currentJavadocParent.getParent(); ParseTree tempParseTreeParent = parseTreeParent.getParent(); while (nextJavadocSibling == null && tempJavadocParent != null) { nextJavadocSibling = (JavadocNodeImpl) JavadocUtils.getNextSibling(tempJavadocParent); nextParseTreeSibling = getNextSibling(tempParseTreeParent); tempJavadocParent = (JavadocNodeImpl) tempJavadocParent.getParent(); tempParseTreeParent = tempParseTreeParent.getParent(); } } currentJavadocParent = nextJavadocSibling; parseTreeParent = nextParseTreeSibling; } } return rootJavadocNode; }
/** * Finds and returns chars till first dot. * * @param textNode node with javadoc text. * @return String with chars till first dot. */ private static String getCharsTillDot(DetailNode textNode) { final StringBuilder result = new StringBuilder(); for (DetailNode child : textNode.getChildren()) { result.append(child.getText()); if (PERIOD.equals(child.getText()) && JavadocUtils.getNextSibling(child).getType() == JavadocTokenTypes.WS) { break; } } return result.toString(); }
/** * Gets token type of ParseTree node from JavadocTokenTypes class. * * @param node ParseTree node. * @return token type from JavadocTokenTypes */ private static int getTokenType(ParseTree node) { final int tokenType; if (node.getChildCount() == 0) { tokenType = ((TerminalNode) node).getSymbol().getType(); } else { final String className = getNodeClassNameWithoutContext(node); final String typeName = CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, className); tokenType = JavadocUtils.getTokenId(typeName); } return tokenType; }
/** * Prints full tree (java + comments + javadoc) of the DetailAST. * * @param ast root DetailAST * @return Full tree */ private static String printJavaAndJavadocTree(DetailAST ast) { final StringBuilder messageBuilder = new StringBuilder(); DetailAST node = ast; while (node != null) { messageBuilder.append(getIndentation(node)).append(getNodeInfo(node)).append(LINE_SEPARATOR); if (node.getType() == TokenTypes.COMMENT_CONTENT && JavadocUtils.isJavadocComment(node.getParent())) { final String javadocTree = parseAndPrintJavadocTree(node); messageBuilder.append(javadocTree); } else { messageBuilder.append(printJavaAndJavadocTree(node.getFirstChild())); } node = node.getNextSibling(); } return messageBuilder.toString(); }
/** * Parses Javadoc comment as DetailNode tree. * * @param javadocCommentAst DetailAST of Javadoc comment * @return DetailNode tree of Javadoc comment */ public ParseStatus parseJavadocAsDetailNode(DetailAST javadocCommentAst) { blockCommentLineNumber = javadocCommentAst.getLineNo(); final String javadocComment = JavadocUtils.getJavadocCommentContent(javadocCommentAst); // Use a new error listener each time to be able to use // one check instance for multiple files to be checked // without getting side effects. errorListener = new DescriptiveErrorListener(); // Log messages should have line number in scope of file, // not in scope of Javadoc comment. // Offset is line number of beginning of Javadoc comment. errorListener.setOffset(javadocCommentAst.getLineNo() - 1); final ParseStatus result = new ParseStatus(); try { final ParseTree parseTree = parseJavadocAsParseTree(javadocComment); final DetailNode tree = convertParseTreeToDetailNode(parseTree); result.setTree(tree); } catch (ParseCancellationException ex) { // If syntax error occurs then message is printed by error listener // and parser throws this runtime exception to stop parsing. // Just stop processing current Javadoc comment. ParseErrorMessage parseErrorMessage = errorListener.getErrorMessage(); // There are cases when antlr error listener does not handle syntax error if (parseErrorMessage == null) { parseErrorMessage = new ParseErrorMessage( javadocCommentAst.getLineNo(), MSG_KEY_UNRECOGNIZED_ANTLR_ERROR, javadocCommentAst.getColumnNo(), ex.getMessage()); } result.setParseErrorMessage(parseErrorMessage); } return result; }