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.util.Set;
24 import java.util.regex.Pattern;
25
26 import org.apache.maven.doxia.macro.MacroExecutionException;
27 import org.apache.maven.doxia.sink.Sink;
28
29 import com.puppycrawl.tools.checkstyle.PropertyType;
30 import com.puppycrawl.tools.checkstyle.XdocsPropertyType;
31 import com.puppycrawl.tools.checkstyle.api.DetailNode;
32 import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
33 import com.puppycrawl.tools.checkstyle.meta.JavadocMetadataScraperUtil;
34 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
35
36
37
38
39 public final class ModuleJavadocParsingUtil {
40
41 public static final String NEWLINE = System.lineSeparator();
42
43 public static final String INDENT_LEVEL_4 = SiteUtil.getNewlineAndIndentSpaces(4);
44
45 public static final String INDENT_LEVEL_6 = SiteUtil.getNewlineAndIndentSpaces(6);
46
47 public static final String INDENT_LEVEL_8 = SiteUtil.getNewlineAndIndentSpaces(8);
48
49 public static final String INDENT_LEVEL_10 = SiteUtil.getNewlineAndIndentSpaces(10);
50
51 public static final String INDENT_LEVEL_12 = SiteUtil.getNewlineAndIndentSpaces(12);
52
53 public static final String INDENT_LEVEL_14 = SiteUtil.getNewlineAndIndentSpaces(14);
54
55 public static final String INDENT_LEVEL_16 = SiteUtil.getNewlineAndIndentSpaces(16);
56
57 public static final String INDENT_LEVEL_18 = SiteUtil.getNewlineAndIndentSpaces(18);
58
59 public static final String INDENT_LEVEL_20 = SiteUtil.getNewlineAndIndentSpaces(20);
60
61 public static final Set<String> HTML_TEXT_FORMAT_TAGS = Set.of("<code>", "<a", "</a>", "<b>",
62 "</b>", "<strong>", "</strong>", "<i>", "</i>", "<em>", "</em>", "<small>", "</small>",
63 "<ins>", "<sub>", "<sup>");
64
65 public static final String NOTES = "Notes:";
66
67 public static final Pattern NOTES_LINE = Pattern.compile("\\s*" + NOTES + "$");
68
69 public static final Pattern NOTES_LINE_WITH_NEWLINE = Pattern.compile("\r?\n\\s?" + NOTES);
70
71
72
73
74 private ModuleJavadocParsingUtil() {
75 }
76
77
78
79
80
81
82
83
84 public static Set<String> getPropertyNames(String moduleName)
85 throws MacroExecutionException {
86 final Object instance = SiteUtil.getModuleInstance(moduleName);
87 final Class<?> clss = instance.getClass();
88
89 return SiteUtil.getPropertiesForDocumentation(clss, instance);
90 }
91
92
93
94
95
96
97
98 private static boolean isStartOfNotesSection(DetailNode htmlElement) {
99 boolean result = false;
100 if (htmlElement != null) {
101 final DetailNode htmlContentNode = JavadocUtil.findFirstToken(
102 htmlElement, JavadocCommentsTokenTypes.HTML_CONTENT);
103
104 result = htmlContentNode != null && JavadocMetadataScraperUtil.isChildNodeTextMatches(
105 htmlContentNode, NOTES_LINE);
106 }
107 return result;
108 }
109
110
111
112
113
114
115
116 public static void writeOutJavadocPortion(String javadocPortion, Sink sink) {
117 final String[] javadocPortionLinesSplit = javadocPortion.split(NEWLINE
118 .replace("\r", ""), -1);
119
120 sink.rawText(javadocPortionLinesSplit[0]);
121 String lastHtmlTag = javadocPortionLinesSplit[0];
122
123 for (int index = 1; index < javadocPortionLinesSplit.length; index++) {
124 final String currentLine = javadocPortionLinesSplit[index].trim();
125 final String processedLine;
126
127 if (currentLine.isEmpty()) {
128 processedLine = NEWLINE;
129 }
130 else if (currentLine.startsWith("<")
131 && !startsWithTextFormattingHtmlTag(currentLine)) {
132
133 processedLine = INDENT_LEVEL_8 + currentLine;
134 lastHtmlTag = currentLine;
135 }
136 else if (lastHtmlTag.contains("<pre")) {
137 final String currentLineWithPreservedIndent = javadocPortionLinesSplit[index]
138 .substring(1);
139
140 processedLine = NEWLINE + currentLineWithPreservedIndent;
141 }
142 else {
143 processedLine = INDENT_LEVEL_10 + currentLine;
144 }
145
146 sink.rawText(processedLine);
147 }
148
149 }
150
151
152
153
154
155
156
157 public static boolean startsWithTextFormattingHtmlTag(String line) {
158 boolean result = false;
159
160 for (String tag : HTML_TEXT_FORMAT_TAGS) {
161 if (line.startsWith(tag)) {
162 result = true;
163 break;
164 }
165 }
166
167 return result;
168 }
169
170
171
172
173
174
175
176 public static String getModuleDescription(DetailNode moduleJavadoc) {
177 final DetailNode descriptionEndNode = getDescriptionEndNode(moduleJavadoc);
178 String result = "";
179 if (descriptionEndNode != null) {
180 result = JavadocMetadataScraperUtil.constructSubTreeText(moduleJavadoc,
181 descriptionEndNode);
182 }
183 return result;
184 }
185
186
187
188
189
190
191
192 public static String getModuleSinceVersion(DetailNode moduleJavadoc) {
193 final DetailNode sinceTagNode = getModuleSinceVersionTagStartNode(moduleJavadoc);
194 String result = "";
195
196 if (sinceTagNode == null) {
197 result = "";
198 }
199 else if (sinceTagNode.getFirstChild() != null) {
200 result = JavadocMetadataScraperUtil.constructSubTreeText(sinceTagNode,
201 sinceTagNode.getFirstChild()).replace("@since ", "");
202 }
203 return result;
204 }
205
206
207
208
209
210
211
212 public static DetailNode getDescriptionEndNode(DetailNode moduleJavadoc) {
213 final DetailNode descriptionEndNode;
214
215 final DetailNode notesStartingNode =
216 getNotesSectionStartNode(moduleJavadoc);
217
218 if (notesStartingNode != null) {
219 descriptionEndNode = notesStartingNode.getPreviousSibling();
220 }
221 else {
222 descriptionEndNode = getNodeBeforeJavadocTags(moduleJavadoc);
223 }
224
225 return descriptionEndNode;
226 }
227
228
229
230
231
232
233
234 public static DetailNode getNotesSectionStartNode(DetailNode moduleJavadoc) {
235 DetailNode notesStartNode = null;
236 if (moduleJavadoc != null) {
237 DetailNode node = moduleJavadoc.getFirstChild();
238
239 while (node != null) {
240 if (isNotesSectionNode(node)) {
241 notesStartNode = node;
242 break;
243 }
244 node = node.getNextSibling();
245 }
246 }
247
248 return notesStartNode;
249 }
250
251
252
253
254
255
256
257 private static boolean isNotesSectionNode(DetailNode node) {
258 boolean result = false;
259 if (node.getType() == JavadocCommentsTokenTypes.HTML_ELEMENT) {
260 if (JavadocUtil.isTag(node, "ul")) {
261 final DetailNode htmlContentNode = JavadocUtil.findFirstToken(
262 node, JavadocCommentsTokenTypes.HTML_CONTENT);
263 result = htmlContentNode != null
264 && isStartOfNotesSection(htmlContentNode.getFirstChild());
265 }
266 else {
267 result = (JavadocUtil.isTag(node, "p")
268 || JavadocUtil.isTag(node, "li"))
269 && isStartOfNotesSection(node);
270 }
271 }
272 return result;
273 }
274
275
276
277
278
279
280
281
282 public static DetailNode getModuleSinceVersionTagStartNode(DetailNode moduleJavadoc) {
283 DetailNode result = null;
284
285 if (moduleJavadoc != null) {
286 result = JavadocUtil.getAllNodesOfType(
287 moduleJavadoc, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG).stream()
288 .filter(javadocTag -> {
289 final DetailNode firstChild = javadocTag.getFirstChild();
290 return firstChild != null
291 && firstChild.getType()
292 == JavadocCommentsTokenTypes.SINCE_BLOCK_TAG;
293 })
294 .findFirst()
295 .orElse(null);
296 }
297 return result;
298 }
299
300
301
302
303
304
305
306
307 public static DetailNode getNodeBeforeJavadocTags(DetailNode moduleJavadoc) {
308 DetailNode nodeBeforeJavadocTags = null;
309
310 if (moduleJavadoc != null) {
311 nodeBeforeJavadocTags = moduleJavadoc.getFirstChild();
312
313 if (nodeBeforeJavadocTags != null) {
314 while (nodeBeforeJavadocTags.getNextSibling() != null
315 && nodeBeforeJavadocTags.getNextSibling().getType()
316 != JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG) {
317
318 nodeBeforeJavadocTags = nodeBeforeJavadocTags.getNextSibling();
319 }
320 }
321 }
322
323 return nodeBeforeJavadocTags;
324 }
325
326
327
328
329
330
331
332 public static String getModuleNotes(DetailNode moduleJavadoc) {
333 final String result;
334
335 final DetailNode notesStartNode = getNotesSectionStartNode(moduleJavadoc);
336
337 if (notesStartNode == null) {
338 result = "";
339 }
340 else {
341 final DetailNode notesEndNode = getNodeBeforeJavadocTags(moduleJavadoc);
342
343 if (notesEndNode == null) {
344 result = "";
345 }
346 else {
347 final String unprocessedNotes = JavadocMetadataScraperUtil.constructSubTreeText(
348 notesStartNode, notesEndNode);
349 result = NOTES_LINE_WITH_NEWLINE.matcher(unprocessedNotes).replaceAll("");
350 }
351 }
352
353 return result;
354 }
355
356
357
358
359
360
361
362 public static boolean isPropertySpecialTokenProp(Field propertyField) {
363 boolean result = false;
364
365 if (propertyField != null) {
366 final XdocsPropertyType fieldXdocAnnotation =
367 propertyField.getAnnotation(XdocsPropertyType.class);
368
369 result = fieldXdocAnnotation != null
370 && fieldXdocAnnotation.value() == PropertyType.TOKEN_ARRAY;
371 }
372
373 return result;
374 }
375
376 }