-
Notifications
You must be signed in to change notification settings - Fork 2.2k
CheckStyleRule-Javadoc-Codesnippet Batch#3, No.7 and No.8 #4920
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
4cac1b9
9dbeae5
b52c5ab
cdf50a0
a41ffdc
e7ea7f2
9f7c156
c3c3571
06380ad
6d6064d
a5e48df
7187b42
340c302
5af60e2
840a047
0c7109b
d5f2576
2f9cb94
a9223ae
f473be0
0746ff7
d5f8359
e97c88c
ea9dc37
464d111
af7f4e8
5f2c597
8cf387c
8cc72ea
f8d2228
451f4bb
513fbe8
3eea6dc
068e7a0
95bfe16
ac69f96
be10e5c
0dcaa7b
99a5903
59e53ba
eb3fa99
36be65c
f38084e
8faecd8
88c6405
89e9a38
5bd8728
7997ae8
ed59769
25e36f8
d66b863
a5bc7fd
6b5f663
f97767a
3e0fbd9
631e111
9a45b7a
b99e398
9b33a3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| package com.azure.tools.checkstyle.checks; | ||
|
|
||
| import com.puppycrawl.tools.checkstyle.api.DetailNode; | ||
| import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes; | ||
| import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck; | ||
| import com.puppycrawl.tools.checkstyle.utils.JavadocUtil; | ||
|
|
||
| import java.util.regex.Pattern; | ||
|
|
||
| /** | ||
| * Requirement of Javadoc annotation @codesnippet check: | ||
| * (1) Use '{@codesnippet ...}' instead of '<code>', '<pre>'. | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| * (2) No multiple lines span at the '{@code ...}'. | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| * (3) naming pattern of codesnippet": | ||
| * 1, For package name, class name, they should all be lower case. | ||
| * For method name, the first letter of method name should be lower case. | ||
| * For parameters, they should only be letters. Concatenate all parameters with dash character. | ||
| * 2, Jonathon will add more requirement specs later. | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| */ | ||
| public class JavadocCodeSnippetCheck extends AbstractJavadocCheck { | ||
|
|
||
| private static final String CLASS_PATH_REGEX = "[a-z.]+"; | ||
| private static final String METHOD_NAME_REGEX = "^[a-z][a-zA-Z]+"; | ||
| private static final String PARAMETERS_REGEX = "[a-zA-Z-]+"; | ||
|
conniey marked this conversation as resolved.
Outdated
|
||
|
|
||
| @Override | ||
| public int[] getDefaultJavadocTokens() { | ||
| return getRequiredJavadocTokens(); | ||
| } | ||
|
|
||
| @Override | ||
| public int[] getRequiredJavadocTokens() { | ||
| return new int[] { | ||
| JavadocTokenTypes.HTML_ELEMENT_START, | ||
| JavadocTokenTypes.JAVADOC_INLINE_TAG | ||
| }; | ||
| } | ||
|
|
||
| @Override | ||
| public void visitJavadocToken(DetailNode token) { | ||
| switch (token.getType()) { | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| case JavadocTokenTypes.HTML_ELEMENT_START: | ||
| checkHtmlElementStart(token); | ||
| break; | ||
| case JavadocTokenTypes.JAVADOC_INLINE_TAG: | ||
| checkJavadocInlineTag(token); | ||
| checkCodeSnippetNaming(token); | ||
| default: | ||
| // Checkstyle complains if there's no default block in switch | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Requirement of rules: | ||
| * (1) No usage of '<code>', '<pre>', use '{@codesnippet ...} instead'. | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| * (2) No multiple lines span at the '{@code ...}' | ||
| * | ||
| * @param htmlElementStartNode | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| */ | ||
| private void checkHtmlElementStart(DetailNode htmlElementStartNode) { | ||
| final DetailNode tagNameNode = JavadocUtil.findFirstToken(htmlElementStartNode, JavadocTokenTypes.HTML_TAG_NAME); | ||
| final String tagName = tagNameNode.getText(); | ||
| if ("code".equals(tagName) || "pre".equals(tagName)) { | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| log(tagNameNode.getLineNumber(), tagNameNode.getColumnNumber(), | ||
| String.format("Do not use <%s> html tag in javadoc. Use <codesnippet> instead.", tagName)); | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Check to see if the JAVADOC_INLINE_TAG node is '@code' tag or '@codesnippet' tag. | ||
| * If the JAVADOC_INLINE_TAG is one of two, check if the tag contains new line or leading asterisk, | ||
| * which implies the tag has spanned in multiple lines. | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| * | ||
| * @param inlineTagNode JAVADOC_INLINE_TAG javadoc node | ||
| */ | ||
| private void checkJavadocInlineTag(DetailNode inlineTagNode) { | ||
| boolean isCodeOrCodeSnippet = false; | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| if (JavadocUtil.findFirstToken(inlineTagNode, JavadocTokenTypes.CODE_LITERAL) != null) { | ||
| isCodeOrCodeSnippet = true; | ||
| } | ||
| isCodeOrCodeSnippet |= isCodeSnippet(inlineTagNode); | ||
| if (!isCodeOrCodeSnippet) { | ||
| return; | ||
| } | ||
|
|
||
| for (final DetailNode child : inlineTagNode.getChildren()) { | ||
| final int childType = child.getType(); | ||
|
|
||
| if (childType == JavadocTokenTypes.NEWLINE || childType == JavadocTokenTypes.LEADING_ASTERISK) { | ||
| log(child.getLineNumber(), child.getColumnNumber(), "No multiple lines in @code annotation"); | ||
| } | ||
|
|
||
| // This code section duplicates the checking on @codesnippet, maven build already failed it | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| if (childType == JavadocTokenTypes.DESCRIPTION | ||
| && (!JavadocUtil.containsInBranch(child, JavadocTokenTypes.NEWLINE) | ||
| || !JavadocUtil.containsInBranch(child, JavadocTokenTypes.LEADING_ASTERISK))) { | ||
| log(child.getLineNumber(), child.getColumnNumber(), "No multiple lines in @codesnippet annotation"); | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| } | ||
| } | ||
|
mssfang marked this conversation as resolved.
|
||
| return; | ||
| } | ||
|
|
||
| /** | ||
| * Check to see if the code snippet name matched the naming pattern. | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you show us an example of a valid naming pattern? And what conditions need to be met for this to fit the naming pattern.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. com.azure.security.keyvault.keys.cryptography.cryptographyclient.encrypt#symmetric-encrypt. More description added.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not a valid naming pattern. The My expectation that this name would actually be
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should enforce that the part after the |
||
| * | ||
| * @param inlineTagNode JAVADOC_INLINE_TAG javadoc node | ||
| */ | ||
| private void checkCodeSnippetNaming(DetailNode inlineTagNode) { | ||
| if (!isCodeSnippet(inlineTagNode)) { | ||
| return; | ||
| } | ||
|
|
||
| final DetailNode descriptionNode = JavadocUtil.findFirstToken(inlineTagNode, JavadocTokenTypes.DESCRIPTION); | ||
| if (descriptionNode == null) { | ||
| return; | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| final DetailNode textNode = JavadocUtil.findFirstToken(descriptionNode, JavadocTokenTypes.TEXT); | ||
| if (textNode == null) { | ||
| return; | ||
| } | ||
|
|
||
| final String namingPattern = textNode.getText(); | ||
| // Verify naming pattern spec: | ||
| // 1, For package name, class name, they should all be lower case; | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| // For method name, the first letter of method name should be lower case. | ||
| // For parameters, they should only be letters. Concatenate all parameters with dash character | ||
| // 2, Jonathon will add more requirement specs later | ||
| if (!isNamingMatch(namingPattern)) { | ||
| log(textNode.getLineNumber(), textNode.getColumnNumber(), | ||
| String.format("Naming pattern mismatch. It should only contain lower case for the package path and class name for matching regular expression ''%s''" + | ||
| " and the first letter of method name must be small case for matching regular expression ''%s'', and the parameters should be all small case for matching " + | ||
| "regular expression ''%s''.", CLASS_PATH_REGEX, METHOD_NAME_REGEX, PARAMETERS_REGEX)); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Find if the given JAVADOC_INLINE_TAG is a @codesnippet tag. | ||
| * | ||
| * @param inlineTagNode JAVADOC_INLINE_TAG javadoc node | ||
| * @return true if it is a '@codesnippet' tag, false otherwise. | ||
| */ | ||
| private boolean isCodeSnippet(DetailNode inlineTagNode) { | ||
| final DetailNode customNameNode = JavadocUtil.findFirstToken(inlineTagNode, JavadocTokenTypes.CUSTOM_NAME); | ||
| if (customNameNode == null) { | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| return false; | ||
| } | ||
| return customNameNode.getText().equals("@codesnippet"); | ||
| } | ||
|
|
||
| /** | ||
| * Find if the given name of code snippet matched the naming pattern specs. | ||
| * | ||
| * @param name code snippet name | ||
| * @return true if match, otherwise, false | ||
| */ | ||
| private boolean isNamingMatch(String name) { | ||
| final String[] str = name.split("#"); | ||
| boolean isCorrectNamingPattern = true; | ||
| // invalid naming pattern | ||
| if (str.length == 0 || str.length > 2) { | ||
| return false; | ||
| } | ||
|
|
||
| final String classPath = str[0]; | ||
| // corner case: empty string will also be false | ||
| if (classPath.isEmpty()) { | ||
| return false; | ||
| } | ||
|
|
||
| final int lastIndexOf = classPath.lastIndexOf("."); | ||
| if (lastIndexOf == -1) { | ||
| return false; | ||
| } | ||
|
|
||
| // full path of the class, check to see if there path only has lower case letters and dot | ||
| final String className = classPath.substring(0, lastIndexOf); | ||
| isCorrectNamingPattern &= Pattern.compile(CLASS_PATH_REGEX).matcher(className).matches(); | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| // method name | ||
| final String methodName = classPath.substring(lastIndexOf + 1); | ||
| isCorrectNamingPattern &= Pattern.compile(METHOD_NAME_REGEX).matcher(methodName).matches(); | ||
|
|
||
| // parameters of method function | ||
| if (str.length == 2){ | ||
|
mssfang marked this conversation as resolved.
Outdated
|
||
| isCorrectNamingPattern &= Pattern.compile(PARAMETERS_REGEX).matcher(str[1]).matches(); | ||
| } | ||
|
|
||
| return isCorrectNamingPattern; | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.