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