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 }