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