Skip to content

Commit

Permalink
Merge 65b26d2 into a539762
Browse files Browse the repository at this point in the history
  • Loading branch information
nrmancuso committed Jan 20, 2021
2 parents a539762 + 65b26d2 commit 57313b7
Show file tree
Hide file tree
Showing 6 changed files with 293 additions and 4 deletions.
Expand Up @@ -23,6 +23,8 @@
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.puppycrawl.tools.checkstyle.StatelessCheck;
Expand Down Expand Up @@ -159,6 +161,12 @@
* Type is {@code java.lang.String[]}.
* Default value is {@code After, AfterClass, Before, BeforeClass, Test}.
* </li>
* <li>
* Property {@code requiredJavadocPhrase} - Specify the comment text pattern which qualifies a
* method as designed for extension. Supports multi-line regex.
* Type is {@code java.util.regex.Pattern}.
* Default value is {@code ".*"}.
* </li>
* </ul>
* <p>
* To configure the check:
Expand Down Expand Up @@ -208,6 +216,66 @@
* }
* </pre>
* <p>
* To configure the check to allow methods which contain a specified comment text
* pattern in their javadoc to be designed for extension.
* </p>
* <pre>
* &lt;module name=&quot;DesignForExtension&quot;&gt;
* &lt;property name=&quot;requiredJavadocPhrase&quot;
* value=&quot;This implementation&quot;/&gt;
* &lt;/module&gt;
* </pre>
* <pre>
* public class A extends B {
* /&#42;&#42;
* &#42; This implementation ...
* &#42;
* &#42;/
* public int foo() { // ok, required javadoc phrase in comment
* return 2;
* }
*
* /&#42;&#42;
* &#42; Do not extend ...
* &#42;
* &#42;/
* public int foo2() {return 8;} // violation, required javadoc phrase not in comment
*
* public int foo3() {return 3;} // violation, required javadoc phrase not in comment
* }
* </pre>
* <p>
* To configure the check to allow methods which contain a specified comment text
* pattern in their javadoc which can span multiple lines
* to be designed for extension.
* </p>
* <pre>
* &lt;module name=&quot;DesignForExtension&quot;&gt;
* &lt;property name=&quot;requiredJavadocPhrase&quot;
* value=&quot;This[\s\S]*implementation&quot;/&gt;
* &lt;/module&gt;
* </pre>
* <pre>
* public class A extends B {
* /&#42;&#42;
* &#42; This
* &#42; implementation ...
* &#42;
* &#42;/
* public int foo() { // ok, required javadoc phrase in comment
* return 2;
* }
*
* /&#42;&#42;
* &#42; Do not extend ...
* &#42;
* &#42;/
* public int foo2() {return 8;} // violation, required javadoc phrase not in comment
*
* public int foo3() {return 3;} // violation, required javadoc phrase not in comment
* }
* </pre>
* <p>
* Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
* </p>
* <p>
Expand Down Expand Up @@ -236,6 +304,12 @@ public class DesignForExtensionCheck extends AbstractCheck {
private Set<String> ignoredAnnotations = Arrays.stream(new String[] {"Test", "Before", "After",
"BeforeClass", "AfterClass", }).collect(Collectors.toSet());

/**
* Specify the comment text pattern which qualifies a method as designed for extension.
* Supports multi-line regex.
*/
private Pattern requiredJavadocPhrase = Pattern.compile(".*");

/**
* Setter to specify annotations which allow the check to skip the method from validation.
*
Expand All @@ -245,6 +319,16 @@ public void setIgnoredAnnotations(String... ignoredAnnotations) {
this.ignoredAnnotations = Arrays.stream(ignoredAnnotations).collect(Collectors.toSet());
}

/**
* Setter to specify the comment text pattern which qualifies a
* method as designed for extension. Supports multi-line regex.
*
* @param requiredJavadocPhrase method annotations.
*/
public void setRequiredJavadocPhrase(Pattern requiredJavadocPhrase) {
this.requiredJavadocPhrase = requiredJavadocPhrase;
}

@Override
public int[] getDefaultTokens() {
return getRequiredTokens();
Expand Down Expand Up @@ -291,7 +375,7 @@ && canBeOverridden(ast)
* @param methodDef method definition token.
* @return true if a method has a javadoc comment.
*/
private static boolean hasJavadocComment(DetailAST methodDef) {
private boolean hasJavadocComment(DetailAST methodDef) {
return hasJavadocCommentOnToken(methodDef, TokenTypes.MODIFIERS)
|| hasJavadocCommentOnToken(methodDef, TokenTypes.TYPE);
}
Expand All @@ -303,7 +387,7 @@ private static boolean hasJavadocComment(DetailAST methodDef) {
* @param tokenType token type.
* @return true if a token has a javadoc comment.
*/
private static boolean hasJavadocCommentOnToken(DetailAST methodDef, int tokenType) {
private boolean hasJavadocCommentOnToken(DetailAST methodDef, int tokenType) {
final DetailAST token = methodDef.findFirstToken(tokenType);
return branchContainsJavadocComment(token);
}
Expand All @@ -314,13 +398,13 @@ private static boolean hasJavadocCommentOnToken(DetailAST methodDef, int tokenTy
* @param token tree token.
* @return true if a javadoc comment exists under the token.
*/
private static boolean branchContainsJavadocComment(DetailAST token) {
private boolean branchContainsJavadocComment(DetailAST token) {
boolean result = false;
DetailAST curNode = token;
while (curNode != null) {
if (curNode.getType() == TokenTypes.BLOCK_COMMENT_BEGIN
&& JavadocUtil.isJavadocComment(curNode)) {
result = true;
result = hasValidJavadocComment(curNode);
break;
}

Expand All @@ -339,6 +423,23 @@ private static boolean branchContainsJavadocComment(DetailAST token) {
return result;
}

/**
* Checks whether a javadoc contains the specified comment pattern that denotes
* a method as designed for extension.
*
* @param detailAST the ast we are checking for possible extension
* @return true if the javadoc of this ast contains the required comment pattern
*/
private boolean hasValidJavadocComment(DetailAST detailAST) {
final String javadocString =
JavadocUtil.getBlockCommentContent(detailAST);

final Matcher requiredJavadocPhraseMatcher =
requiredJavadocPhrase.matcher(javadocString);

return requiredJavadocPhraseMatcher.find();
}

/**
* Checks whether a methods is native.
*
Expand Down
Expand Up @@ -128,6 +128,12 @@
<description>Specify annotations which allow the check to
skip the method from validation.</description>
</property>
<property default-value=".*"
name="requiredJavadocPhrase"
type="java.util.regex.Pattern">
<description>Specify the comment text pattern which qualifies a
method as designed for extension. Supports multi-line regex.</description>
</property>
</properties>
<message-keys>
<message-key key="design.forExtension"/>
Expand Down
Expand Up @@ -138,4 +138,29 @@ public void testDesignForExtensionRecords() throws Exception {
getNonCompilablePath("InputDesignForExtensionRecords.java"), expected);
}

@Test
public void testRequiredJavadocPhrase() throws Exception {
final DefaultConfiguration checkConfig = createModuleConfig(DesignForExtensionCheck.class);
checkConfig.addAttribute("requiredJavadocPhrase", "This implementation");
final String className = "InputDesignForExtensionRequiredJavadocPhrase";
final String[] expected = {
"37:5: " + getCheckMessage(MSG_KEY, className, "foo5"),
"44:5: " + getCheckMessage(MSG_KEY, className, "foo8"),
"47:5: " + getCheckMessage(MSG_KEY, className, "foo9"),
"63:5: " + getCheckMessage(MSG_KEY, className, "foo12"),
};
verify(checkConfig, getPath("InputDesignForExtensionRequiredJavadocPhrase.java"), expected);
}

@Test
public void testRequiredJavadocPhraseMultiLine() throws Exception {
final DefaultConfiguration checkConfig = createModuleConfig(DesignForExtensionCheck.class);
checkConfig.addAttribute("requiredJavadocPhrase", "This[\\s\\S]*implementation");
final String className = "InputDesignForExtensionRequiredJavadocPhraseMultiLine";
final String[] expected = {
"19:5: " + getCheckMessage(MSG_KEY, className, "foo2"),
};
verify(checkConfig, getPath("InputDesignForExtensionRequiredJavadocPhraseMultiLine.java"),
expected);
}
}
@@ -0,0 +1,66 @@
package com.puppycrawl.tools.checkstyle.checks.design.designforextension;

/* Config:
* requiredJavadocPhrase = "This implementation"
*
*/
public class InputDesignForExtensionRequiredJavadocPhrase {

/**
* This implementation is for <p> some html code
* </p>.
*
* @param a
* @param b
* @return sum
*/
public int foo1(int a, int b) {return a + b;} // ok, required comment pattern in javadoc

/**
* This implementation is required for ...
*
* @param a
* @param b
* @return sum
*/
public int foo2(int a, int b) {return a + b;} // ok, required comment pattern in javadoc

/** This implementation is for ... */
public int foo3(int a, int b) {return a + b;} // ok, required comment pattern in javadoc

/**
* This implementation ...
*/
public int foo4(int a, int b) {return a + b;} // ok, required comment pattern in javadoc

/** This method can safely be overridden. */
public int foo5(int a, int b) {return a + b;} // violation

public final int foo6(int a) {return a - 2;} // ok, final

protected final int foo7(int a) {return a - 2;} // ok, final

/** */
public int foo8(int a) {return a - 2;} // violation

// This implementation
public int foo9(int a, int b) {return a + b;} // violation, not javadoc

@Deprecated
protected final int foo10(int a) {return a - 2;} // ok, deprecated

/**
* This implementation is for <p> some html code
* </p>.
*
* @param a
* @param b
* @return sum
*/
public int foo11(int a, int b) {return a + b;} // ok, required comment pattern in javadoc

/**This method can safely be overridden. */
public int foo12(int a, int b) { // violation
return a + b;
}
}
@@ -0,0 +1,22 @@
package com.puppycrawl.tools.checkstyle.checks.design.designforextension;

/* Config:
* requiredJavadocPhrase = "This[\s\S]*implementation"
*
*/
public class InputDesignForExtensionRequiredJavadocPhraseMultiLine {
/**
* This
* implementation ..
*/
public int foo1(int a, int b) {
return a * b;
}

/**
* This method can safely be overridden.
*/
public int foo2(int a, int b) { // violation
return a + b;
}
}
69 changes: 69 additions & 0 deletions src/xdocs/config_design.xml
Expand Up @@ -157,6 +157,16 @@ public abstract class Plant {
<td><code>After, AfterClass, Before, BeforeClass, Test</code></td>
<td>7.2</td>
</tr>
<tr>
<td>requiredJavadocPhrase</td>
<td>
Specify the comment text pattern which qualifies a method as designed for extension.
Supports multi-line regex.
</td>
<td><a href="property_types.html#pattern">Pattern</a></td>
<td><code>&quot;.*&quot;</code></td>
<td>8.40</td>
</tr>
</table>
</div>
</subsection>
Expand Down Expand Up @@ -211,6 +221,65 @@ public class FooTest {
}

public int foo4() {return 4;} // violation
}
</source>
<p>
To configure the check to allow methods which contain a specified comment text
pattern in their javadoc to be designed for extension.
</p>
<source>
&lt;module name=&quot;DesignForExtension&quot;&gt;
&lt;property name=&quot;requiredJavadocPhrase&quot; value=&quot;This implementation&quot;/&gt;
&lt;/module&gt;
</source>
<source>
public class A extends B {
/&#42;&#42;
&#42; This implementation ...
&#42;
&#42;/
public int foo() { // ok, required javadoc phrase in comment
return 2;
}

/&#42;&#42;
&#42; Do not extend ...
&#42;
&#42;/
public int foo2() {return 8;} // violation, required javadoc phrase not in comment

public int foo3() {return 3;} // violation, required javadoc phrase not in comment
}
</source>
<p>
To configure the check to allow methods which contain a specified comment text
pattern in their javadoc which can span multiple lines
to be designed for extension.
</p>
<source>
&lt;module name=&quot;DesignForExtension&quot;&gt;
&lt;property name=&quot;requiredJavadocPhrase&quot;
value=&quot;This[\s\S]*implementation&quot;/&gt;
&lt;/module&gt;
</source>
<source>
public class A extends B {
/&#42;&#42;
&#42; This
&#42; implementation ...
&#42;
&#42;/
public int foo() { // ok, required javadoc phrase in comment
return 2;
}

/&#42;&#42;
&#42; Do not extend ...
&#42;
&#42;/
public int foo2() {return 8;} // violation, required javadoc phrase not in comment

public int foo3() {return 3;} // violation, required javadoc phrase not in comment
}
</source>
</subsection>
Expand Down

0 comments on commit 57313b7

Please sign in to comment.