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