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