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
220
221
222 public static Set<Field> getCheckMessages(Class<?> module, boolean deepScan) {
223 final Set<Field> checkstyleMessages = new HashSet<>();
224
225
226 final Field[] fields = module.getDeclaredFields();
227
228 for (Field field : fields) {
229 if (field.getName().startsWith("MSG_")) {
230 checkstyleMessages.add(field);
231 }
232 }
233
234
235 final Class<?> superModule = module.getSuperclass();
236
237 if (superModule != null && (deepScan || shouldScanDeepClassForMessages(superModule))) {
238 checkstyleMessages.addAll(getCheckMessages(superModule, deepScan));
239 }
240
241
242 if (module == RegexpMultilineCheck.class) {
243 checkstyleMessages.addAll(getCheckMessages(MultilineDetector.class, deepScan));
244 }
245 else if (module == RegexpSinglelineCheck.class
246 || module == RegexpSinglelineJavaCheck.class) {
247 checkstyleMessages.addAll(getCheckMessages(SinglelineDetector.class, deepScan));
248 }
249
250 return checkstyleMessages;
251 }
252
253
254
255
256
257
258
259 private static boolean shouldScanDeepClassForMessages(Class<?> superModule) {
260 return superModule == AbstractNameCheck.class
261 || superModule == AbstractAccessControlNameCheck.class
262 || superModule == AbstractParenPadCheck.class
263 || superModule == AbstractSuperCheck.class;
264 }
265
266
267
268
269
270
271
272
273
274
275
276 public static String getCheckMessage(Class<?> module, Locale locale, String messageKey,
277 Object... arguments) {
278 String checkMessage;
279 try {
280 final Properties pr = new Properties();
281 if (locale.equals(Locale.ENGLISH)) {
282 pr.load(module.getResourceAsStream("messages.properties"));
283 }
284 else {
285 pr.load(module
286 .getResourceAsStream("messages_" + locale.getLanguage() + ".properties"));
287 }
288 final MessageFormat formatter = new MessageFormat(pr.getProperty(messageKey), locale);
289 checkMessage = formatter.format(arguments);
290 }
291 catch (IOException ignored) {
292 checkMessage = null;
293 }
294 return checkMessage;
295 }
296
297 public static String getTokenText(int[] tokens, int... subtractions) {
298 final String tokenText;
299 if (subtractions.length == 0 && Arrays.equals(tokens, TokenUtil.getAllTokenIds())) {
300 tokenText = "TokenTypes.";
301 }
302 else {
303 final StringBuilder result = new StringBuilder(50);
304 boolean first = true;
305
306 for (int token : tokens) {
307 boolean found = false;
308
309 for (int subtraction : subtractions) {
310 if (subtraction == token) {
311 found = true;
312 break;
313 }
314 }
315
316 if (found) {
317 continue;
318 }
319
320 if (first) {
321 first = false;
322 }
323 else {
324 result.append(", ");
325 }
326
327 result.append(TokenUtil.getTokenName(token));
328 }
329
330 if (!result.isEmpty()) {
331 result.append('.');
332 }
333
334 tokenText = result.toString();
335 }
336 return tokenText;
337 }
338
339 public static Set<String> getTokenNameSet(int... tokens) {
340 final Set<String> result = new HashSet<>();
341
342 for (int token : tokens) {
343 result.add(TokenUtil.getTokenName(token));
344 }
345
346 return result;
347 }
348
349 public static String getJavadocTokenText(int[] tokens, int... subtractions) {
350 final StringBuilder result = new StringBuilder(50);
351 boolean first = true;
352
353 for (int token : tokens) {
354 boolean found = false;
355
356 for (int subtraction : subtractions) {
357 if (subtraction == token) {
358 found = true;
359 break;
360 }
361 }
362
363 if (found) {
364 continue;
365 }
366
367 if (first) {
368 first = false;
369 }
370 else {
371 result.append(", ");
372 }
373
374 result.append(JavadocUtil.getTokenName(token));
375 }
376
377 if (!result.isEmpty()) {
378 result.append('.');
379 }
380
381 return result.toString();
382 }
383
384 public static String getLineSeparatorForFile(String filepath, Charset charset)
385 throws IOException {
386 final OptionalInt endOfLineChar = new FileText(new File(filepath), charset.name())
387 .getFullText()
388 .chars()
389 .filter(character -> character == '\r' || character == '\n')
390 .findFirst();
391
392 final String result;
393 if (endOfLineChar.isPresent() && endOfLineChar.getAsInt() == '\r') {
394 result = CRLF;
395 }
396 else {
397 result = "\n";
398 }
399 return result;
400 }
401 }