/** * Returns the tags in a javadoc comment. Only finds throws, exception, param, return and see * tags. * * @param comment the Javadoc comment * @return the tags found */ private static List<JavadocTag> getMethodTags(TextBlock comment) { final String[] lines = comment.getText(); final List<JavadocTag> tags = Lists.newArrayList(); int currentLine = comment.getStartLineNo() - 1; final int startColumnNumber = comment.getStartColNo(); for (int i = 0; i < lines.length; i++) { currentLine++; final Matcher javadocArgMatcher = MATCH_JAVADOC_ARG.matcher(lines[i]); final Matcher javadocNoargMatcher = MATCH_JAVADOC_NOARG.matcher(lines[i]); final Matcher noargCurlyMatcher = MATCH_JAVADOC_NOARG_CURLY.matcher(lines[i]); final Matcher argMultilineStart = MATCH_JAVADOC_ARG_MULTILINE_START.matcher(lines[i]); final Matcher noargMultilineStart = MATCH_JAVADOC_NOARG_MULTILINE_START.matcher(lines[i]); if (javadocArgMatcher.find()) { final int col = calculateTagColumn(javadocArgMatcher, i, startColumnNumber); tags.add( new JavadocTag( currentLine, col, javadocArgMatcher.group(1), javadocArgMatcher.group(2))); } else if (javadocNoargMatcher.find()) { final int col = calculateTagColumn(javadocNoargMatcher, i, startColumnNumber); tags.add(new JavadocTag(currentLine, col, javadocNoargMatcher.group(1))); } else if (noargCurlyMatcher.find()) { final int col = calculateTagColumn(noargCurlyMatcher, i, startColumnNumber); tags.add(new JavadocTag(currentLine, col, noargCurlyMatcher.group(1))); } else if (argMultilineStart.find()) { final int col = calculateTagColumn(argMultilineStart, i, startColumnNumber); tags.addAll(getMultilineArgTags(argMultilineStart, col, lines, i, currentLine)); } else if (noargMultilineStart.find()) { tags.addAll(getMultilineNoArgTags(noargMultilineStart, lines, i, currentLine)); } } return tags; }
@Override public void beginTree(DetailAST rootAST) { final Map<Integer, TextBlock> cppComments = getFileContents().getCppComments(); final Map<Integer, List<TextBlock>> cComments = getFileContents().getCComments(); final Set<Integer> lines = Sets.newHashSet(); lines.addAll(cppComments.keySet()); lines.addAll(cComments.keySet()); for (Integer lineNo : lines) { final String line = getLines()[lineNo - 1]; final String lineBefore; final TextBlock comment; if (cppComments.containsKey(lineNo)) { comment = cppComments.get(lineNo); lineBefore = line.substring(0, comment.getStartColNo()); } else { final List<TextBlock> commentList = cComments.get(lineNo); comment = commentList.get(commentList.size() - 1); lineBefore = line.substring(0, comment.getStartColNo()); // do not check comment which doesn't end line if (comment.getText().length == 1 && !line.substring(comment.getEndColNo() + 1).trim().isEmpty()) { continue; } } if (!regexp.matcher(lineBefore).find() && !isLegalComment(comment)) { log(lineNo, MSG_KEY); } } }
/** * Appends the suppressions in a collection of comments to the full set of suppression tags. * * @param comments the set of comments. */ private void tagSuppressions(Collection<TextBlock> comments) { for (final TextBlock comment : comments) { final int startLineNo = comment.getStartLineNo(); final String[] text = comment.getText(); tagCommentLine(text[0], startLineNo); for (int i = 1; i < text.length; i++) { tagCommentLine(text[i], startLineNo + i); } } }
@Override public void visitToken(DetailAST ast) { if (this.currentVersion == null) { // If not current version is set, just ignore this check return; } switch (ast.getType()) { case TokenTypes.PACKAGE_DEF: // Save the package FullIdent ident = FullIdent.createFullIdent(ast.getLastChild().getPreviousSibling()); this.packageName = ident.getText(); return; case TokenTypes.CLASS_DEF: case TokenTypes.INTERFACE_DEF: this.classOrInterfaceName = ast.findFirstToken(TokenTypes.IDENT).getText(); break; } if (AnnotationUtility.containsAnnotation(ast)) { DetailAST holder = AnnotationUtility.getAnnotationHolder(ast); for (DetailAST annotation : findAllTokens(holder, TokenTypes.ANNOTATION)) { String annotationName = annotation.findFirstToken(TokenTypes.IDENT).getText(); if (annotationName.equals("Unstable")) { FileContents contents = getFileContents(); String annotatedElementName = ast.findFirstToken(TokenTypes.IDENT).getText(); // Get the Javadoc before the annotation in order to locate a @Since annotation and to // extract // the XWiki version mentioned there. List<String> sinceVersions = Collections.emptyList(); TextBlock cmt = contents.getJavadocBefore(ast.getLineNo()); if (cmt != null) { sinceVersions = extractSinceVersionsFromJavadoc(cmt.getText(), annotation, annotatedElementName); } if (sinceVersions.isEmpty()) { log( annotation.getLineNo(), annotation.getColumnNo(), String.format( "There is an @Unstable " + "annotation for [%s] but the @since javadoc tag is missing, you must add it!", computeElementName(annotatedElementName))); return; } checkSinceVersions(sinceVersions, annotation, annotatedElementName); } } } }
/** * Checks to see if the text block contains a inheritDoc tag. * * @param javadoc the javadoc of the AST * @return true if contains the tag */ private static boolean containsJavadocTag(final TextBlock javadoc) { if (javadoc == null) { return false; } final String[] lines = javadoc.getText(); for (final String line : lines) { final Matcher matchInheritDoc = MATCH_INHERITDOC.matcher(line); if (matchInheritDoc.find()) { return true; } } return false; }
/** * Checks if given comment is legal (single-line and matches to the pattern). * * @param comment comment to check. * @return true if the comment if legal. */ private boolean isLegalComment(final TextBlock comment) { final boolean legal; // multi-line comment can not be legal if (legalComment == null || comment.getStartLineNo() != comment.getEndLineNo()) { legal = false; } else { String commentText = comment.getText()[0]; // remove chars which start comment commentText = commentText.substring(2); // if this is a C-style comment we need to remove its end if (commentText.endsWith("*/")) { commentText = commentText.substring(0, commentText.length() - 2); } commentText = commentText.trim(); legal = legalComment.matcher(commentText).find(); } return legal; }
/** * Gets validTags from a given piece of Javadoc. * * @param cmt the Javadoc comment to process. * @param tagType the type of validTags we're interested in * @return all standalone validTags from the given javadoc. */ public static JavadocTags getJavadocTags(TextBlock cmt, JavadocTagType tagType) { final String[] text = cmt.getText(); final List<JavadocTag> tags = Lists.newArrayList(); final List<InvalidJavadocTag> invalidTags = Lists.newArrayList(); Pattern blockTagPattern = Pattern.compile("/\\*{2,}\\s*@(\\p{Alpha}+)\\s"); for (int i = 0; i < text.length; i++) { final String s = text[i]; final Matcher blockTagMatcher = blockTagPattern.matcher(s); if ((tagType == JavadocTagType.ALL || tagType == JavadocTagType.BLOCK) && blockTagMatcher.find()) { final String tagName = blockTagMatcher.group(1); String content = s.substring(blockTagMatcher.end(1)); if (content.endsWith("*/")) { content = content.substring(0, content.length() - 2); } final int line = cmt.getStartLineNo() + i; int col = blockTagMatcher.start(1) - 1; if (i == 0) { col += cmt.getStartColNo(); } if (JavadocTagInfo.isValidName(tagName)) { tags.add(new JavadocTag(line, col, tagName, content.trim())); } else { invalidTags.add(new InvalidJavadocTag(line, col, tagName)); } } // No block tag, so look for inline validTags else if (tagType == JavadocTagType.ALL || tagType == JavadocTagType.INLINE) { // Match Javadoc text after comment characters final Pattern commentPattern = Pattern.compile("^\\s*(?:/\\*{2,}|\\*+)\\s*(.*)"); final Matcher commentMatcher = commentPattern.matcher(s); final String commentContents; final int commentOffset; // offset including comment characters if (!commentMatcher.find()) { commentContents = s; // No leading asterisks, still valid commentOffset = 0; } else { commentContents = commentMatcher.group(1); commentOffset = commentMatcher.start(1) - 1; } final Pattern tagPattern = Pattern.compile(".*?\\{@(\\p{Alpha}+)\\s+(.*?)\\}"); final Matcher tagMatcher = tagPattern.matcher(commentContents); while (tagMatcher.find()) { final String tagName = tagMatcher.group(1); final String tagValue = tagMatcher.group(2).trim(); final int line = cmt.getStartLineNo() + i; int col = commentOffset + tagMatcher.start(1) - 1; if (i == 0) { col += cmt.getStartColNo(); } if (JavadocTagInfo.isValidName(tagName)) { tags.add(new JavadocTag(line, col, tagName, tagValue)); } else { invalidTags.add(new InvalidJavadocTag(line, col, tagName)); } // else Error: Unexpected match count for inline Javadoc // tag! } } blockTagPattern = Pattern.compile("^\\s*\\**\\s*@(\\p{Alpha}+)\\s"); } return new JavadocTags(tags, invalidTags); }