Skip to content

Commit

Permalink
Issue #14424: Add option to skip validation based on list of annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
Lmh-java committed Mar 22, 2024
1 parent 781182d commit f763b2e
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 1 deletion.
Expand Up @@ -19,10 +19,14 @@

package com.puppycrawl.tools.checkstyle.checks.design;

import java.util.Collections;
import java.util.Set;

import com.puppycrawl.tools.checkstyle.StatelessCheck;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;

/**
* <p>
Expand Down Expand Up @@ -51,6 +55,14 @@
* }
* }
* </pre>
* <ul>
* <li>
* Property {@code ignoreAnnotatedBy} - Ignore classes annotated
* with the specified annotation(s).
* Type is {@code java.lang.String[]}.
* Default value is {@code ""}.
* </li>
* </ul>
* <p>
* Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
* </p>
Expand All @@ -74,6 +86,11 @@ public class HideUtilityClassConstructorCheck extends AbstractCheck {
*/
public static final String MSG_KEY = "hide.utility.class";

/**
* Ignore classes annotated with the specified annotation(s).
*/
private Set<String> ignoreAnnotatedBy = Collections.emptySet();

@Override
public int[] getDefaultTokens() {
return getRequiredTokens();
Expand All @@ -91,8 +108,9 @@ public int[] getRequiredTokens() {

@Override
public void visitToken(DetailAST ast) {
// classes with specific annotations should be skipped
// abstract class could not have private constructor
if (!isAbstract(ast)) {
if (!isAbstract(ast) && !shouldIgnoreClass(ast)) {
final boolean hasStaticModifier = isStatic(ast);

final Details details = new Details(ast);
Expand Down Expand Up @@ -142,6 +160,26 @@ private static boolean isStatic(DetailAST ast) {
.findFirstToken(TokenTypes.LITERAL_STATIC) != null;
}

/**
* Setter to ignore classes annotated with the specified annotation(s).
*
* @param annotationNames specified annotation(s)
* @since 10.15.0
*/
public void setIgnoreAnnotatedBy(String... annotationNames) {
ignoreAnnotatedBy = Set.of(annotationNames);
}

/**
* Checks if class is annotated by specific annotation(s) to skip.
*
* @param ast class to check
* @return true if annotated by ignored annotations
*/
private boolean shouldIgnoreClass(DetailAST ast) {
return AnnotationUtil.containsAnnotation(ast, ignoreAnnotatedBy);
}

/**
* Details of class that are required for validation.
*/
Expand Down
Expand Up @@ -30,6 +30,12 @@
}
}
&lt;/pre&gt;</description>
<properties>
<property default-value="" name="ignoreAnnotatedBy" type="java.lang.String[]">
<description>Ignore classes annotated
with the specified annotation(s).</description>
</property>
</properties>
<message-keys>
<message-key key="hide.utility.class"/>
</message-keys>
Expand Down
Expand Up @@ -146,4 +146,26 @@ public void testGetAcceptableTokens() {
.isEqualTo(expected);
}

@Test
public void testIgnoreAnnotatedBy() throws Exception {
final String[] expected = {
"30:1: " + getCheckMessage(MSG_KEY),
};
verifyWithInlineConfigParser(
getPath("InputHideUtilityClassConstructorIgnoreAnnotationBy.java"),
expected
);
}

@Test
public void testIgnoreAnnotatedByFullQualifier() throws Exception {
final String[] expected = {
"9:1: " + getCheckMessage(MSG_KEY),
};
verifyWithInlineConfigParser(
getPath("InputHideUtilityClassConstructor"
+ "IgnoreAnnotationByFullyQualifiedName.java"),
expected
);
}
}
@@ -0,0 +1,46 @@
/*
HideUtilityClassConstructor
ignoreAnnotatedBy = Skip, SkipWithParam, SkipWithAnnotationAsParam
*/

package com.puppycrawl.tools.checkstyle.checks.design.hideutilityclassconstructor;

@Skip
public class InputHideUtilityClassConstructorIgnoreAnnotationBy {
public static void func() {}
}

@SkipWithParam(name = "tool1")
class ToolClass1 {
public static void func() {}
}

@SkipWithAnnotationAsParam(skip = @Skip)
class ToolClass2 {
public static void func() {}
}

@CommonAnnot
@Skip
class ToolClass3 {
public static void func() {}
}

@CommonAnnot // violation
class ToolClass4 {
public static void func() {}
}


@interface Skip {}

@interface SkipWithParam {
String name();
}

@interface SkipWithAnnotationAsParam {
Skip skip();
}

@interface CommonAnnot {}
@@ -0,0 +1,19 @@
/*
HideUtilityClassConstructor
ignoreAnnotatedBy = java.lang.Deprecated
*/

package com.puppycrawl.tools.checkstyle.checks.design.hideutilityclassconstructor;

@Deprecated // violation
public class InputHideUtilityClassConstructorIgnoreAnnotationByFullyQualifiedName {
public static void func() {}
}

@java.lang.Deprecated
class DeprecatedClass {
public static void func() {}
}

@interface Deprecated {}
Expand Up @@ -41,4 +41,13 @@ public void testExample1() throws Exception {

verifyWithInlineConfigParser(getPath("Example1.java"), expected);
}

@Test
public void testExample2() throws Exception {
final String[] expected = {
"14:1: " + getCheckMessage(MSG_KEY),
};

verifyWithInlineConfigParser(getPath("Example2.java"), expected);
}
}
@@ -0,0 +1,48 @@
/*xml
<module name="Checker">
<module name="TreeWalker">
<module name="HideUtilityClassConstructor">
<property name="ignoreAnnotatedBy" value="SkipMe, java.lang.Deprecated" />
</module>
</module>
</module>
*/

package com.puppycrawl.tools.checkstyle.checks.design.hideutilityclassconstructor;

// xdoc section -- start
class Example2 { // violation

public Example2() {

}

public static void fun() {

}
}

@SkipMe
class IgnoredClass1 { // ok, ignored by annotation
public IgnoredClass1() {

}

public static void fun() {

}
}

@java.lang.Deprecated
class IgnoredClass2 { // ok, ignored by annotation
public IgnoredClass2() {

}

public static void fun() {

}
}

@interface SkipMe {}
// xdoc section -- end
75 changes: 75 additions & 0 deletions src/xdocs/checks/design/hideutilityclassconstructor.xml
Expand Up @@ -42,6 +42,27 @@ public class StringUtils // not final to allow subclassing
</source>
</subsection>

<subsection name="Properties" id="Properties">
<div class="wrapper">
<table>
<tr>
<th>name</th>
<th>description</th>
<th>type</th>
<th>default value</th>
<th>since</th>
</tr>
<tr>
<td>ignoreAnnotatedBy</td>
<td>Ignore classes annotated with the specified annotation(s).</td>
<td><a href="../../property_types.html#String.5B.5D">String[]</a></td>
<td><code>{}</code></td>
<td>10.15.0</td>
</tr>
</table>
</div>
</subsection>

<subsection name="Examples" id="Examples">
<p id="Example1-config">
To configure the check:
Expand Down Expand Up @@ -85,6 +106,60 @@ class UtilityClass { // violation
static float f;
}
</source>

<p id="Example2-config">
To configure the check:
</p>
<source>
&lt;module name=&quot;Checker&quot;&gt;
&lt;module name=&quot;TreeWalker&quot;&gt;
&lt;module name=&quot;HideUtilityClassConstructor&quot;&gt;
&lt;property name=&quot;ignoreAnnotatedBy&quot; value=&quot;SkipMe, java.lang.Deprecated&quot; /&gt;
&lt;/module&gt;
&lt;/module&gt;
&lt;/module&gt;
</source>
<p id="Example2-code">Example:</p>
<source>
class Example2 { // violation

public Example2() {

}

public static void fun() {

}
}

@SkipMe
class IgnoredClass1 { // ok, ignored by annotation
public IgnoredClass1() {

}

public static void fun() {

}
}

@java.lang.Deprecated
class IgnoredClass2 { // ok, ignored by annotation
public IgnoredClass2() {

}

public static void fun() {

}
}

@interface SkipMe {}
</source>
<p>
Note: Annotation names specified in the <code>ignoreAnnotatedBy</code> property
must be an exact match for the annotation name on the class.
</p>
</subsection>

<subsection name="Example of Usage" id="Example_of_Usage">
Expand Down
28 changes: 28 additions & 0 deletions src/xdocs/checks/design/hideutilityclassconstructor.xml.template
Expand Up @@ -42,6 +42,15 @@ public class StringUtils // not final to allow subclassing
</source>
</subsection>

<subsection name="Properties" id="Properties">
<div class="wrapper">
<macro name="properties">
<param name="modulePath"
value="src/main/java/com/puppycrawl/tools/checkstyle/checks/design/HideUtilityClassConstructorCheck.java"/>
</macro>
</div>
</subsection>

<subsection name="Examples" id="Examples">
<p id="Example1-config">
To configure the check:
Expand All @@ -57,6 +66,25 @@ public class StringUtils // not final to allow subclassing
value="resources/com/puppycrawl/tools/checkstyle/checks/design/hideutilityclassconstructor/Example1.java"/>
<param name="type" value="code"/>
</macro>

<p id="Example2-config">
To configure the check:
</p>
<macro name="example">
<param name="path"
value="resources/com/puppycrawl/tools/checkstyle/checks/design/hideutilityclassconstructor/Example2.java"/>
<param name="type" value="config"/>
</macro>
<p id="Example2-code">Example:</p>
<macro name="example">
<param name="path"
value="resources/com/puppycrawl/tools/checkstyle/checks/design/hideutilityclassconstructor/Example2.java"/>
<param name="type" value="code"/>
</macro>
<p>
Note: Annotation names specified in the <code>ignoreAnnotatedBy</code> property
must be an exact match for the annotation name on the class.
</p>
</subsection>

<subsection name="Example of Usage" id="Example_of_Usage">
Expand Down

0 comments on commit f763b2e

Please sign in to comment.