View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.javadoc;
21  
22  import java.util.Arrays;
23  import java.util.Set;
24  import java.util.stream.Collectors;
25  
26  import com.puppycrawl.tools.checkstyle.StatelessCheck;
27  import com.puppycrawl.tools.checkstyle.api.DetailNode;
28  import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
29  import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
30  
31  /**
32   * <div>
33   * Checks that Javadoc comments avoid unnecessary {@code {@link}} and {@code {@linkplain}} tags
34   * for APIs that are considered well-known. Linking well-known APIs can make comments harder to
35   * read without adding much value for the reader.
36   * </div>
37   *
38   * <p>
39   * This check reports {@code {@link}} references to configured well-known APIs.
40   * Two properties are supported:
41   * {@code wellKnownQualifiedPackages} and {@code wellKnownSimpleNames}.
42   * </p>
43   *
44   * <p>
45   * Both properties are needed because Checkstyle does not resolve Javadoc link targets.
46   * For example, {@code java.lang.String} contains the package name, so it can be
47   * matched through {@code wellKnownQualifiedPackages}. However, {@code String}
48   * only contains the simple name {@code String}, so it needs to be matched through
49   * {@code wellKnownSimpleNames}. Resolution of imports is not a solution since
50   * {@code java.lang} is implicitly imported.
51   * </p>
52   *
53   * <p>
54   * For {@code wellKnownQualifiedPackages}, only references to classes that are
55   * direct members of a well-known package are reported. References to a member
56   * (for example, {@code String#length()}), a nested class (for example,
57   * {@code System.Logger}), a subpackage (for example, {@code java.lang.ref.WeakReference}),
58   * and a package itself (for example, {@code java.lang.ref}) are not reported.
59   * </p>
60   *
61   * @since 14.1.0
62   */
63  @StatelessCheck
64  public class JavadocLinkWellKnownApiCheck extends AbstractJavadocCheck {
65  
66      /**
67       * A key is pointing to the warning message text in "messages.properties"
68       * file.
69       */
70      public static final String MSG_WELL_KNOWN_API = "javadoc.wellKnownApi";
71  
72      /**
73       * A key is pointing to the warning message text in "messages.properties"
74       * file.
75       */
76      public static final String MSG_WELL_KNOWN_PACKAGE = "javadoc.wellKnownPackage";
77  
78      /**
79       * Dot.
80       */
81      private static final char DOT = '.';
82  
83      /**
84       * Package names whose fully qualified API references should not be linked.
85       */
86      private Set<String> wellKnownQualifiedPackages = Set.of("java.lang");
87  
88      /**
89       * Simple API names that should not be linked.
90       */
91      private Set<String> wellKnownSimpleNames = Set.of("String");
92  
93      /**
94       * Creates a new {@code JavadocLinkWellKnownApiCheck} instance.
95       */
96      public JavadocLinkWellKnownApiCheck() {
97          // no code by default
98      }
99  
100     @Override
101     public int[] getDefaultJavadocTokens() {
102         return getRequiredJavadocTokens();
103     }
104 
105     @Override
106     public int[] getRequiredJavadocTokens() {
107         return new int[] {
108             JavadocCommentsTokenTypes.LINK_INLINE_TAG,
109             JavadocCommentsTokenTypes.LINKPLAIN_INLINE_TAG,
110         };
111     }
112 
113     /**
114      * Setter to specify package names whose fully qualified API references should not be
115      * linked.
116      *
117      * @param values user's values.
118      * @since 14.1.0
119      */
120     public final void setWellKnownQualifiedPackages(String... values) {
121         wellKnownQualifiedPackages = Arrays.stream(values).collect(Collectors.toUnmodifiableSet());
122     }
123 
124     /**
125      * Setter to specify simple API names that should not be linked.
126      *
127      * @param values user's values.
128      * @since 14.1.0
129      */
130     public final void setWellKnownSimpleNames(String... values) {
131         wellKnownSimpleNames = Arrays.stream(values).collect(Collectors.toUnmodifiableSet());
132     }
133 
134     @Override
135     public void visitJavadocToken(DetailNode ast) {
136         final DetailNode referenceNode = JavadocUtil.findFirstToken(ast,
137                 JavadocCommentsTokenTypes.REFERENCE);
138         if (JavadocUtil.findFirstToken(referenceNode,
139                 JavadocCommentsTokenTypes.MEMBER_REFERENCE) == null) {
140             final String apiName = referenceNode.getFirstChild().getText();
141             if (isWellKnownQualified(apiName)) {
142                 log(ast, MSG_WELL_KNOWN_PACKAGE, apiName);
143             }
144             else if (wellKnownSimpleNames.contains(apiName)) {
145                 log(ast, MSG_WELL_KNOWN_API, apiName);
146             }
147         }
148     }
149 
150     /**
151      * Checks whether the given API name belongs to a well-known qualified package.
152      *
153      * @param apiName the API name to check
154      * @return true if the API name belongs to a well-known qualified package
155      */
156     private boolean isWellKnownQualified(String apiName) {
157         boolean result = false;
158         for (String packageName : wellKnownQualifiedPackages) {
159             final String prefix = packageName + DOT;
160             final int prefixLength = prefix.length();
161             if (apiName.startsWith(prefix)
162                     && apiName.length() > prefixLength
163                     && Character.isUpperCase(apiName.charAt(prefixLength))
164                     && apiName.indexOf(DOT, prefixLength) == -1) {
165                 result = true;
166                 break;
167             }
168         }
169         return result;
170     }
171 
172 }