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.site;
21
22 import java.lang.reflect.Field;
23 import java.nio.file.Path;
24 import java.nio.file.Paths;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.regex.Pattern;
33 import java.util.stream.Collectors;
34
35 import org.apache.maven.doxia.macro.AbstractMacro;
36 import org.apache.maven.doxia.macro.Macro;
37 import org.apache.maven.doxia.macro.MacroExecutionException;
38 import org.apache.maven.doxia.macro.MacroRequest;
39 import org.apache.maven.doxia.module.xdoc.XdocSink;
40 import org.apache.maven.doxia.sink.Sink;
41 import org.codehaus.plexus.component.annotations.Component;
42
43 import com.puppycrawl.tools.checkstyle.PropertyType;
44 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
45 import com.puppycrawl.tools.checkstyle.api.DetailNode;
46 import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck;
47 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
48 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
49 import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
50
51
52
53
54 @Component(role = Macro.class, hint = "properties")
55 public class PropertiesMacro extends AbstractMacro {
56
57
58
59
60 public static final String EMPTY = "empty";
61
62
63 public static final Set<String> NON_BASE_TOKEN_PROPERTIES = Collections.unmodifiableSet(
64 Arrays.stream(new String[] {
65 "AtclauseOrder - target",
66 "DescendantToken - limitedTokens",
67 "IllegalType - memberModifiers",
68 "MagicNumber - constantWaiverParentToken",
69 "MultipleStringLiterals - ignoreOccurrenceContext",
70 }).collect(Collectors.toSet()));
71
72
73 private static final Pattern COMMA_SPACE_PATTERN = Pattern.compile(", ");
74
75
76 private static final Pattern CHECK_PATTERN = Pattern.compile("Check$");
77
78
79 private static final String CURLY_BRACKET = "{}";
80
81
82 private static final String PROPERTY_TYPES_XML = "property_types.xml";
83
84
85 private static final String URL_F = "%s#%s";
86
87
88 private static final String CODE_START = "<code>";
89
90
91 private static final String CODE_END = "</code>";
92
93
94
95
96
97 private static final String TOKENS_PROPERTY = SiteUtil.TOKENS;
98
99
100 private static String currentModuleName = "";
101
102
103 private static Path currentModulePath = Paths.get("");
104
105 @Override
106 public void execute(Sink sink, MacroRequest request) throws MacroExecutionException {
107
108 if (!(sink instanceof XdocSink)) {
109 throw new MacroExecutionException("Expected Sink to be an XdocSink.");
110 }
111
112 final String modulePath = (String) request.getParameter("modulePath");
113
114 configureGlobalProperties(modulePath);
115
116 writePropertiesTable((XdocSink) sink);
117 }
118
119
120
121
122
123
124
125 private static void configureGlobalProperties(String modulePath)
126 throws MacroExecutionException {
127 final Path modulePathObj = Paths.get(modulePath);
128 currentModulePath = modulePathObj;
129 final Path fileNamePath = modulePathObj.getFileName();
130
131 if (fileNamePath == null) {
132 throw new MacroExecutionException(
133 "Invalid modulePath '" + modulePath + "': No file name present.");
134 }
135
136 currentModuleName = CommonUtil.getFileNameWithoutExtension(
137 fileNamePath.toString());
138 }
139
140
141
142
143
144
145
146
147 private static void writePropertiesTable(XdocSink sink)
148 throws MacroExecutionException {
149 sink.table();
150 sink.setInsertNewline(false);
151 sink.tableRows(null, false);
152 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_12);
153 writeTableHeaderRow(sink);
154 writeTablePropertiesRows(sink);
155 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_10);
156 sink.tableRows_();
157 sink.table_();
158 sink.setInsertNewline(true);
159 }
160
161
162
163
164
165
166 private static void writeTableHeaderRow(Sink sink) {
167 sink.tableRow();
168 writeTableHeaderCell(sink, "name");
169 writeTableHeaderCell(sink, "description");
170 writeTableHeaderCell(sink, "type");
171 writeTableHeaderCell(sink, "default value");
172 writeTableHeaderCell(sink, "since");
173 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_12);
174 sink.tableRow_();
175 }
176
177
178
179
180
181
182
183 private static void writeTableHeaderCell(Sink sink, String text) {
184 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_14);
185 sink.tableHeaderCell();
186 sink.text(text);
187 sink.tableHeaderCell_();
188 }
189
190
191
192
193
194
195
196
197 private static void writeTablePropertiesRows(Sink sink)
198 throws MacroExecutionException {
199 final Object instance = SiteUtil.getModuleInstance(currentModuleName);
200 final Class<?> clss = instance.getClass();
201
202 final Set<String> properties = SiteUtil.getPropertiesForDocumentation(clss, instance);
203 final Map<String, DetailNode> propertiesJavadocs = SiteUtil
204 .getPropertiesJavadocs(properties, currentModuleName, currentModulePath);
205
206 final List<String> orderedProperties = orderProperties(properties);
207
208 for (String property : orderedProperties) {
209 try {
210 final DetailNode propertyJavadoc = propertiesJavadocs.get(property);
211 final DetailNode currentModuleJavadoc =
212 SiteUtil.getModuleJavadoc(currentModuleName, currentModulePath);
213 writePropertyRow(sink, property, propertyJavadoc, instance, currentModuleJavadoc);
214 }
215
216 catch (Exception exc) {
217 final String message = String.format(Locale.ROOT,
218 "Exception while handling moduleName: %s propertyName: %s",
219 currentModuleName, property);
220 throw new MacroExecutionException(message, exc);
221 }
222 }
223 }
224
225
226
227
228
229
230
231
232 private static List<String> orderProperties(Set<String> properties) {
233
234 final List<String> orderProperties = new LinkedList<>(properties);
235
236 if (orderProperties.remove(TOKENS_PROPERTY)) {
237 orderProperties.add(TOKENS_PROPERTY);
238 }
239 if (orderProperties.remove(SiteUtil.JAVADOC_TOKENS)) {
240 orderProperties.add(SiteUtil.JAVADOC_TOKENS);
241 }
242 return List.copyOf(orderProperties);
243
244 }
245
246
247
248
249
250
251
252
253
254
255
256
257 private static void writePropertyRow(Sink sink, String propertyName,
258 DetailNode propertyJavadoc, Object instance,
259 DetailNode moduleJavadoc)
260 throws MacroExecutionException {
261 final Field field = SiteUtil.getField(instance.getClass(), propertyName);
262
263 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_12);
264 sink.tableRow();
265
266 writePropertyNameCell(sink, propertyName);
267 writePropertyDescriptionCell(sink, propertyName, propertyJavadoc);
268 writePropertyTypeCell(sink, propertyName, field, instance);
269 writePropertyDefaultValueCell(sink, propertyName, field, instance);
270 writePropertySinceVersionCell(
271 sink, propertyName, moduleJavadoc, propertyJavadoc);
272
273 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_12);
274 sink.tableRow_();
275 }
276
277
278
279
280
281
282
283 private static void writePropertyNameCell(Sink sink, String propertyName) {
284 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_14);
285 sink.tableCell();
286 sink.text(propertyName);
287 sink.tableCell_();
288 }
289
290
291
292
293
294
295
296
297
298 private static void writePropertyDescriptionCell(Sink sink, String propertyName,
299 DetailNode propertyJavadoc)
300 throws MacroExecutionException {
301 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_14);
302 sink.tableCell();
303 final String description = SiteUtil
304 .getPropertyDescription(propertyName, propertyJavadoc, currentModuleName);
305
306 sink.rawText(description);
307 sink.tableCell_();
308 }
309
310
311
312
313
314
315
316
317
318
319
320 private static void writePropertyTypeCell(Sink sink, String propertyName,
321 Field field, Object instance)
322 throws MacroExecutionException {
323 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_14);
324 sink.tableCell();
325
326 if (SiteUtil.TOKENS.equals(propertyName)) {
327 final AbstractCheck check = (AbstractCheck) instance;
328 if (check.getRequiredTokens().length == 0
329 && Arrays.equals(check.getAcceptableTokens(), TokenUtil.getAllTokenIds())) {
330 sink.text("set of any supported");
331 writeLink(sink);
332 }
333 else {
334 final List<String> configurableTokens = SiteUtil
335 .getDifference(check.getAcceptableTokens(),
336 check.getRequiredTokens())
337 .stream()
338 .map(TokenUtil::getTokenName)
339 .toList();
340 sink.text("subset of tokens");
341
342 writeTokensList(sink, configurableTokens, SiteUtil.PATH_TO_TOKEN_TYPES, true);
343 }
344 }
345 else if (SiteUtil.JAVADOC_TOKENS.equals(propertyName)) {
346 final AbstractJavadocCheck check = (AbstractJavadocCheck) instance;
347 final List<String> configurableTokens = SiteUtil
348 .getDifference(check.getAcceptableJavadocTokens(),
349 check.getRequiredJavadocTokens())
350 .stream()
351 .map(JavadocUtil::getTokenName)
352 .toList();
353 sink.text("subset of javadoc tokens");
354 writeTokensList(sink, configurableTokens, SiteUtil.PATH_TO_JAVADOC_TOKEN_TYPES, true);
355 }
356 else {
357 final String type = SiteUtil.getType(field, propertyName, currentModuleName, instance);
358 if (PropertyType.TOKEN_ARRAY.getDescription().equals(type)) {
359 processLinkForTokenTypes(sink);
360 }
361 else {
362 final String relativePathToPropertyTypes =
363 SiteUtil.getLinkToDocument(currentModuleName, PROPERTY_TYPES_XML);
364 final String escapedType = type
365 .replace("[", ".5B")
366 .replace("]", ".5D");
367
368 final String url =
369 String.format(Locale.ROOT, URL_F, relativePathToPropertyTypes, escapedType);
370
371 sink.link(url);
372 sink.text(type);
373 sink.link_();
374 }
375 }
376 sink.tableCell_();
377 }
378
379
380
381
382
383
384
385 private static void processLinkForTokenTypes(Sink sink)
386 throws MacroExecutionException {
387 final String link =
388 SiteUtil.getLinkToDocument(currentModuleName, SiteUtil.PATH_TO_TOKEN_TYPES);
389
390 sink.text("subset of tokens ");
391 sink.link(link);
392 sink.text("TokenTypes");
393 sink.link_();
394 }
395
396
397
398
399
400
401
402 private static void writeLink(Sink sink)
403 throws MacroExecutionException {
404 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_16);
405 final String link =
406 SiteUtil.getLinkToDocument(currentModuleName, SiteUtil.PATH_TO_TOKEN_TYPES);
407 sink.link(link);
408 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_20);
409 sink.text(SiteUtil.TOKENS);
410 sink.link_();
411 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_14);
412 }
413
414
415
416
417
418
419
420
421
422
423 private static void writeTokensList(Sink sink, List<String> tokens, String tokenTypesLink,
424 boolean printDotAtTheEnd)
425 throws MacroExecutionException {
426 for (int index = 0; index < tokens.size(); index++) {
427 final String token = tokens.get(index);
428 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_16);
429 if (index != 0) {
430 sink.text(SiteUtil.COMMA_SPACE);
431 }
432 writeLinkToToken(sink, tokenTypesLink, token);
433 }
434 if (tokens.isEmpty()) {
435 sink.rawText(CODE_START);
436 sink.text(EMPTY);
437 sink.rawText(CODE_END);
438 }
439 else if (printDotAtTheEnd) {
440 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_18);
441 sink.text(SiteUtil.DOT);
442 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_14);
443 }
444 else {
445 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_14);
446 }
447 }
448
449
450
451
452
453
454
455
456
457 private static void writeLinkToToken(Sink sink, String document, String tokenName)
458 throws MacroExecutionException {
459 final String link = SiteUtil.getLinkToDocument(currentModuleName, document)
460 + "#" + tokenName;
461 sink.link(link);
462 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_20);
463 sink.text(tokenName);
464 sink.link_();
465 }
466
467
468
469
470
471
472
473
474
475
476 private static void writePropertyDefaultValueCell(Sink sink, String propertyName,
477 Field field, Object instance)
478 throws MacroExecutionException {
479 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_14);
480 sink.tableCell();
481
482 if (SiteUtil.TOKENS.equals(propertyName)) {
483 final AbstractCheck check = (AbstractCheck) instance;
484 if (check.getRequiredTokens().length == 0
485 && Arrays.equals(check.getDefaultTokens(), TokenUtil.getAllTokenIds())) {
486 sink.text(SiteUtil.TOKEN_TYPES);
487 }
488 else {
489 final List<String> configurableTokens = SiteUtil
490 .getDifference(check.getDefaultTokens(),
491 check.getRequiredTokens())
492 .stream()
493 .map(TokenUtil::getTokenName)
494 .toList();
495 writeTokensList(sink, configurableTokens, SiteUtil.PATH_TO_TOKEN_TYPES, true);
496 }
497 }
498 else if (SiteUtil.JAVADOC_TOKENS.equals(propertyName)) {
499 final AbstractJavadocCheck check = (AbstractJavadocCheck) instance;
500 final List<String> configurableTokens = SiteUtil
501 .getDifference(check.getDefaultJavadocTokens(),
502 check.getRequiredJavadocTokens())
503 .stream()
504 .map(JavadocUtil::getTokenName)
505 .toList();
506 writeTokensList(sink, configurableTokens, SiteUtil.PATH_TO_JAVADOC_TOKEN_TYPES, true);
507 }
508 else {
509 final String defaultValue = getDefaultValue(propertyName, field, instance);
510 final String checkName = CHECK_PATTERN
511 .matcher(instance.getClass().getSimpleName()).replaceAll("");
512
513 final boolean isSpecialTokenProp = NON_BASE_TOKEN_PROPERTIES.stream()
514 .anyMatch(tokenProp -> tokenProp.equals(checkName + " - " + propertyName));
515
516 if (isSpecialTokenProp && !CURLY_BRACKET.equals(defaultValue)) {
517 final List<String> defaultValuesList =
518 Arrays.asList(COMMA_SPACE_PATTERN.split(defaultValue));
519 writeTokensList(sink, defaultValuesList, SiteUtil.PATH_TO_TOKEN_TYPES, false);
520 }
521 else {
522 sink.rawText(CODE_START);
523 sink.text(defaultValue);
524 sink.rawText(CODE_END);
525 }
526 }
527
528 sink.tableCell_();
529 }
530
531
532
533
534
535
536
537
538
539
540 private static String getDefaultValue(String propertyName, Field field, Object instance)
541 throws MacroExecutionException {
542 final String result;
543
544 if (field != null) {
545 result = SiteUtil.getDefaultValue(
546 propertyName, field, instance, currentModuleName);
547 }
548 else {
549 final Class<?> fieldClass = SiteUtil.getPropertyClass(propertyName, instance);
550
551 if (fieldClass.isArray()) {
552 result = CURLY_BRACKET;
553 }
554 else {
555 result = "null";
556 }
557 }
558 return result;
559 }
560
561
562
563
564
565
566
567
568
569
570 private static void writePropertySinceVersionCell(Sink sink, String propertyName,
571 DetailNode moduleJavadoc,
572 DetailNode propertyJavadoc)
573 throws MacroExecutionException {
574 sink.rawText(ModuleJavadocParsingUtil.INDENT_LEVEL_14);
575 sink.tableCell();
576 final String sinceVersion = SiteUtil.getPropertySinceVersion(
577 currentModuleName, moduleJavadoc, propertyName, propertyJavadoc);
578 sink.text(sinceVersion);
579 sink.tableCell_();
580 }
581 }