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 index = 0; index < nodeList.getLength(); index++) {
139 final Node currentNode = nodeList.item(index);
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)
192 throws IOException {
193 final ClassPath classPath = ClassPath.from(loader);
194 return classPath.getTopLevelClassesRecursive(packageName).stream()
195 .map(ClassPath.ClassInfo::load)
196 .filter(ModuleReflectionUtil::isCheckstyleModule)
197 .filter(CheckUtil::isFromAllowedPackages)
198 .collect(Collectors.toUnmodifiableSet());
199 }
200
201
202
203
204
205
206
207 private static boolean isFromAllowedPackages(Class<?> cls) {
208 final String canonicalName = cls.getCanonicalName();
209 return !canonicalName.startsWith("com.puppycrawl.tools.checkstyle.packageobjectfactory")
210 && !canonicalName.startsWith("com.puppycrawl.tools.checkstyle.internal.testmodules")
211 && !canonicalName.startsWith("com.puppycrawl.tools.checkstyle.site");
212 }
213
214
215
216
217
218
219
220 public static Set<Field> getCheckMessagesWithoutDeepScan(Class<?> module) {
221 return getCheckMessages(module, ScanMode.NO_DEEP_SCAN);
222 }
223
224
225
226
227
228
229
230 public static Set<Field> getCheckMessagesWithDeepScan(Class<?> module) {
231 return getCheckMessages(module, ScanMode.DEEP_SCAN);
232 }
233
234
235
236
237
238
239
240
241 private static Set<Field> getCheckMessages(Class<?> module, ScanMode scanMode) {
242 final Set<Field> checkstyleMessages = new HashSet<>();
243
244
245 final Field[] fields = module.getDeclaredFields();
246
247 for (Field field : fields) {
248 if (field.getName().startsWith("MSG_")) {
249 checkstyleMessages.add(field);
250 }
251 }
252
253
254 final Class<?> superModule = module.getSuperclass();
255
256 if (superModule != null
257 && (scanMode == ScanMode.DEEP_SCAN
258 || shouldScanDeepClassForMessages(superModule))) {
259 checkstyleMessages.addAll(getCheckMessages(superModule, scanMode));
260 }
261
262
263 if (module == RegexpMultilineCheck.class) {
264 checkstyleMessages.addAll(getCheckMessages(MultilineDetector.class, scanMode));
265 }
266 else if (module == RegexpSinglelineCheck.class
267 || module == RegexpSinglelineJavaCheck.class) {
268 checkstyleMessages.addAll(getCheckMessages(SinglelineDetector.class, scanMode));
269 }
270
271 return checkstyleMessages;
272 }
273
274
275
276
277
278
279
280 private static boolean shouldScanDeepClassForMessages(Class<?> superModule) {
281 return superModule == AbstractNameCheck.class
282 || superModule == AbstractAccessControlNameCheck.class
283 || superModule == AbstractParenPadCheck.class
284 || superModule == AbstractSuperCheck.class;
285 }
286
287
288
289
290
291
292
293
294
295
296
297 public static String getCheckMessage(Class<?> module, Locale locale, String messageKey,
298 Object... arguments) {
299 String checkMessage;
300 try {
301 final Properties pr = new Properties();
302 if (locale.equals(Locale.ENGLISH)) {
303 pr.load(module.getResourceAsStream("messages.properties"));
304 }
305 else {
306 pr.load(module
307 .getResourceAsStream("messages_" + locale.getLanguage() + ".properties"));
308 }
309 final MessageFormat formatter = new MessageFormat(pr.getProperty(messageKey), locale);
310 checkMessage = formatter.format(arguments);
311 }
312 catch (IOException ignored) {
313 checkMessage = null;
314 }
315 return checkMessage;
316 }
317
318 public static String getTokenText(int[] tokens, int... subtractions) {
319 final String tokenText;
320 if (subtractions.length == 0 && Arrays.equals(tokens, TokenUtil.getAllTokenIds())) {
321 tokenText = "TokenTypes.";
322 }
323 else {
324 final StringBuilder result = new StringBuilder(50);
325 boolean first = true;
326
327 for (int token : tokens) {
328 boolean found = false;
329
330 for (int subtraction : subtractions) {
331 if (subtraction == token) {
332 found = true;
333 break;
334 }
335 }
336
337 if (found) {
338 continue;
339 }
340
341 if (first) {
342 first = false;
343 }
344 else {
345 result.append(", ");
346 }
347
348 result.append(TokenUtil.getTokenName(token));
349 }
350
351 if (!result.isEmpty()) {
352 result.append('.');
353 }
354
355 tokenText = result.toString();
356 }
357 return tokenText;
358 }
359
360 public static Set<String> getTokenNameSet(int... tokens) {
361 final Set<String> result = new HashSet<>();
362
363 for (int token : tokens) {
364 result.add(TokenUtil.getTokenName(token));
365 }
366
367 return result;
368 }
369
370 public static String getJavadocTokenText(int[] tokens, int... subtractions) {
371 final StringBuilder result = new StringBuilder(50);
372 boolean first = true;
373
374 for (int token : tokens) {
375 boolean found = false;
376
377 for (int subtraction : subtractions) {
378 if (subtraction == token) {
379 found = true;
380 break;
381 }
382 }
383
384 if (found) {
385 continue;
386 }
387
388 if (first) {
389 first = false;
390 }
391 else {
392 result.append(", ");
393 }
394
395 result.append(JavadocUtil.getTokenName(token));
396 }
397
398 if (!result.isEmpty()) {
399 result.append('.');
400 }
401
402 return result.toString();
403 }
404
405 public static String getLineSeparatorForFile(String filepath, Charset charset)
406 throws IOException {
407 final OptionalInt endOfLineChar = new FileText(new File(filepath), charset.name())
408 .getFullText()
409 .chars()
410 .filter(character -> character == '\r' || character == '\n')
411 .findFirst();
412
413 final String result;
414 if (endOfLineChar.isPresent() && endOfLineChar.getAsInt() == '\r') {
415 result = CRLF;
416 }
417 else {
418 result = "\n";
419 }
420 return result;
421 }
422
423
424
425
426 private enum ScanMode {
427
428
429 DEEP_SCAN,
430
431 NO_DEEP_SCAN
432
433 }
434
435 }