1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package com.puppycrawl.tools.checkstyle.internal.utils;
21
22 import java.io.File;
23 import java.io.IOException;
24 import java.lang.reflect.Field;
25 import java.nio.charset.Charset;
26 import java.text.MessageFormat;
27 import java.util.Arrays;
28 import java.util.HashSet;
29 import java.util.Locale;
30 import java.util.Properties;
31 import java.util.Set;
32 import java.util.stream.Collectors;
33
34 import javax.xml.parsers.DocumentBuilder;
35 import javax.xml.parsers.DocumentBuilderFactory;
36
37 import org.w3c.dom.Document;
38 import org.w3c.dom.Element;
39 import org.w3c.dom.Node;
40 import org.w3c.dom.NodeList;
41
42 import com.google.common.reflect.ClassPath;
43 import com.puppycrawl.tools.checkstyle.api.FileText;
44 import com.puppycrawl.tools.checkstyle.checks.coding.AbstractSuperCheck;
45 import com.puppycrawl.tools.checkstyle.checks.naming.AbstractAccessControlNameCheck;
46 import com.puppycrawl.tools.checkstyle.checks.naming.AbstractNameCheck;
47 import com.puppycrawl.tools.checkstyle.checks.regexp.RegexpMultilineCheck;
48 import com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineCheck;
49 import com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck;
50 import com.puppycrawl.tools.checkstyle.checks.whitespace.AbstractParenPadCheck;
51 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
52 import com.puppycrawl.tools.checkstyle.utils.ModuleReflectionUtil;
53 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
54
55 public final class CheckUtil {
56
57 public static final String CRLF = "\r\n";
58
59 private CheckUtil() {
60 }
61
62 public static Set<String> getConfigCheckStyleModules() {
63 return getCheckStyleModulesReferencedInConfig("config/checkstyle-checks.xml");
64 }
65
66 public static Set<String> getConfigSunStyleModules() {
67 return getCheckStyleModulesReferencedInConfig("src/main/resources/sun_checks.xml");
68 }
69
70 public static Set<String> getConfigGoogleStyleModules() {
71 return getCheckStyleModulesReferencedInConfig("src/main/resources/google_checks.xml");
72 }
73
74
75
76
77
78
79
80
81 public static Set<String> getSimpleNames(Set<Class<?>> checks) {
82 return checks.stream().map(check -> {
83 String name = check.getSimpleName();
84
85 if (name.endsWith("Check")) {
86 name = name.substring(0, name.length() - 5);
87 }
88
89 return name;
90 }).collect(Collectors.toCollection(HashSet::new));
91 }
92
93
94
95
96
97
98
99
100 private static Set<String> getCheckStyleModulesReferencedInConfig(String configFilePath) {
101 try {
102 final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
103
104
105
106 factory.setNamespaceAware(false);
107 factory.setValidating(false);
108 factory.setFeature("http://xml.org/sax/features/namespaces", false);
109 factory.setFeature("http://xml.org/sax/features/validation", false);
110 factory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar",
111 false);
112 factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",
113 false);
114
115 final DocumentBuilder builder = factory.newDocumentBuilder();
116 final Document document = builder.parse(new File(configFilePath));
117
118
119
120
121
122 document.getDocumentElement().normalize();
123
124 final NodeList nodeList = document.getElementsByTagName("module");
125
126 final Set<String> checksReferencedInCheckstyleChecksXml = new HashSet<>();
127 for (int i = 0; i < nodeList.getLength(); i++) {
128 final Node currentNode = nodeList.item(i);
129 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
130 final Element module = (Element) currentNode;
131 final String checkName = module.getAttribute("name");
132 checksReferencedInCheckstyleChecksXml.add(checkName);
133 }
134 }
135 return checksReferencedInCheckstyleChecksXml;
136 }
137 catch (Exception exception) {
138 throw new IllegalStateException(exception);
139 }
140 }
141
142
143
144
145
146
147
148 public static Set<Class<?>> getCheckstyleChecks() throws IOException {
149 final ClassLoader loader = Thread.currentThread()
150 .getContextClassLoader();
151 final String packageName = "com.puppycrawl.tools.checkstyle";
152 return getCheckstyleModulesRecursive(packageName, loader).stream()
153 .filter(ModuleReflectionUtil::isCheckstyleTreeWalkerCheck)
154 .collect(Collectors.toUnmodifiableSet());
155 }
156
157
158
159
160
161
162
163 public static Set<Class<?>> getCheckstyleModules() throws IOException {
164 final ClassLoader loader = Thread.currentThread()
165 .getContextClassLoader();
166 final String packageName = "com.puppycrawl.tools.checkstyle";
167 return getCheckstyleModulesRecursive(packageName, loader);
168 }
169
170
171
172
173
174
175
176
177
178
179 private static Set<Class<?>> getCheckstyleModulesRecursive(
180 String packageName, ClassLoader loader) throws IOException {
181 final ClassPath classPath = ClassPath.from(loader);
182 return classPath.getTopLevelClassesRecursive(packageName).stream()
183 .map(ClassPath.ClassInfo::load)
184 .filter(ModuleReflectionUtil::isCheckstyleModule)
185 .filter(CheckUtil::isFromAllowedPackages)
186 .collect(Collectors.toUnmodifiableSet());
187 }
188
189
190
191
192
193
194
195 private static boolean isFromAllowedPackages(Class<?> cls) {
196 final String canonicalName = cls.getCanonicalName();
197 return !canonicalName.startsWith("com.puppycrawl.tools.checkstyle.packageobjectfactory")
198 && !canonicalName.startsWith("com.puppycrawl.tools.checkstyle.internal.testmodules")
199 && !canonicalName.startsWith("com.puppycrawl.tools.checkstyle.site");
200 }
201
202
203
204
205
206
207
208
209
210
211
212 public static Set<Field> getCheckMessages(Class<?> module, boolean deepScan)
213 throws ClassNotFoundException {
214 final Set<Field> checkstyleMessages = new HashSet<>();
215
216
217 final Field[] fields = module.getDeclaredFields();
218
219 for (Field field : fields) {
220 if (field.getName().startsWith("MSG_")) {
221 checkstyleMessages.add(field);
222 }
223 }
224
225
226 final Class<?> superModule = module.getSuperclass();
227
228 if (superModule != null && (deepScan || shouldScanDeepClassForMessages(superModule))) {
229 checkstyleMessages.addAll(getCheckMessages(superModule, deepScan));
230 }
231
232
233 if (module == RegexpMultilineCheck.class) {
234 checkstyleMessages.addAll(getCheckMessages(Class
235 .forName("com.puppycrawl.tools.checkstyle.checks.regexp.MultilineDetector"),
236 deepScan));
237 }
238 else if (module == RegexpSinglelineCheck.class
239 || module == RegexpSinglelineJavaCheck.class) {
240 checkstyleMessages.addAll(getCheckMessages(Class
241 .forName("com.puppycrawl.tools.checkstyle.checks.regexp.SinglelineDetector"),
242 deepScan));
243 }
244
245 return checkstyleMessages;
246 }
247
248
249
250
251
252
253
254 private static boolean shouldScanDeepClassForMessages(Class<?> superModule) {
255 return superModule == AbstractNameCheck.class
256 || superModule == AbstractAccessControlNameCheck.class
257 || superModule == AbstractParenPadCheck.class
258 || superModule == AbstractSuperCheck.class;
259 }
260
261
262
263
264
265
266
267
268
269
270
271 public static String getCheckMessage(Class<?> module, Locale locale, String messageKey,
272 Object... arguments) {
273 String checkMessage;
274 try {
275 final Properties pr = new Properties();
276 if (locale == Locale.ENGLISH) {
277 pr.load(module.getResourceAsStream("messages.properties"));
278 }
279 else {
280 pr.load(module
281 .getResourceAsStream("messages_" + locale.getLanguage() + ".properties"));
282 }
283 final MessageFormat formatter = new MessageFormat(pr.getProperty(messageKey), locale);
284 checkMessage = formatter.format(arguments);
285 }
286 catch (IOException ignored) {
287 checkMessage = null;
288 }
289 return checkMessage;
290 }
291
292 public static String getTokenText(int[] tokens, int... subtractions) {
293 final String tokenText;
294 if (subtractions.length == 0 && Arrays.equals(tokens, TokenUtil.getAllTokenIds())) {
295 tokenText = "TokenTypes.";
296 }
297 else {
298 final StringBuilder result = new StringBuilder(50);
299 boolean first = true;
300
301 for (int token : tokens) {
302 boolean found = false;
303
304 for (int subtraction : subtractions) {
305 if (subtraction == token) {
306 found = true;
307 break;
308 }
309 }
310
311 if (found) {
312 continue;
313 }
314
315 if (first) {
316 first = false;
317 }
318 else {
319 result.append(", ");
320 }
321
322 result.append(TokenUtil.getTokenName(token));
323 }
324
325 if (result.length() != 0) {
326 result.append('.');
327 }
328
329 tokenText = result.toString();
330 }
331 return tokenText;
332 }
333
334 public static Set<String> getTokenNameSet(int... tokens) {
335 final Set<String> result = new HashSet<>();
336
337 for (int token : tokens) {
338 result.add(TokenUtil.getTokenName(token));
339 }
340
341 return result;
342 }
343
344 public static String getJavadocTokenText(int[] tokens, int... subtractions) {
345 final StringBuilder result = new StringBuilder(50);
346 boolean first = true;
347
348 for (int token : tokens) {
349 boolean found = false;
350
351 for (int subtraction : subtractions) {
352 if (subtraction == token) {
353 found = true;
354 break;
355 }
356 }
357
358 if (found) {
359 continue;
360 }
361
362 if (first) {
363 first = false;
364 }
365 else {
366 result.append(", ");
367 }
368
369 result.append(JavadocUtil.getTokenName(token));
370 }
371
372 if (result.length() != 0) {
373 result.append('.');
374 }
375
376 return result.toString();
377 }
378
379 public static String getLineSeparatorForFile(String filepath, Charset charset)
380 throws IOException {
381 final boolean[] crFound = {false};
382 new FileText(new File(filepath), charset.name())
383 .getFullText()
384 .chars()
385 .peek(character -> {
386 if (character == '\r') {
387 crFound[0] = true;
388 }
389 })
390 .filter(character -> character == '\n')
391 .findFirst();
392
393 final String result;
394 if (crFound[0]) {
395 result = CRLF;
396 }
397 else {
398 result = "\n";
399 }
400 return result;
401 }
402 }