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 final DetailNode htmlContentNode = JavadocUtil.findFirstToken(
100 htmlElement, JavadocCommentsTokenTypes.HTML_CONTENT);
101
102 return htmlContentNode != null && JavadocMetadataScraperUtil.isChildNodeTextMatches(
103 htmlContentNode, NOTES_LINE);
104 }
105
106
107
108
109
110
111
112 public static void writeOutJavadocPortion(String javadocPortion, Sink sink) {
113 final String[] javadocPortionLinesSplit = javadocPortion.split(NEWLINE
114 .replace("\r", ""));
115
116 sink.rawText(javadocPortionLinesSplit[0]);
117 String lastHtmlTag = javadocPortionLinesSplit[0];
118
119 for (int index = 1; index < javadocPortionLinesSplit.length; index++) {
120 final String currentLine = javadocPortionLinesSplit[index].trim();
121 final String processedLine;
122
123 if (currentLine.isEmpty()) {
124 processedLine = NEWLINE;
125 }
126 else if (currentLine.startsWith("<")
127 && !startsWithTextFormattingHtmlTag(currentLine)) {
128
129 processedLine = INDENT_LEVEL_8 + currentLine;
130 lastHtmlTag = currentLine;
131 }
132 else if (lastHtmlTag.contains("<pre")) {
133 final String currentLineWithPreservedIndent = javadocPortionLinesSplit[index]
134 .substring(1);
135
136 processedLine = NEWLINE + currentLineWithPreservedIndent;
137 }
138 else {
139 processedLine = INDENT_LEVEL_10 + currentLine;
140 }
141
142 sink.rawText(processedLine);
143 }
144
145 }
146
147
148
149
150
151
152
153 public static boolean startsWithTextFormattingHtmlTag(String line) {
154 boolean result = false;
155
156 for (String tag : HTML_TEXT_FORMAT_TAGS) {
157 if (line.startsWith(tag)) {
158 result = true;
159 break;
160 }
161 }
162
163 return result;
164 }
165
166
167
168
169
170
171
172 public static String getModuleDescription(DetailNode moduleJavadoc) {
173 final DetailNode descriptionEndNode = getDescriptionEndNode(moduleJavadoc);
174
175 return JavadocMetadataScraperUtil.constructSubTreeText(moduleJavadoc, descriptionEndNode);
176 }
177
178
179
180
181
182
183
184 public static String getModuleSinceVersion(DetailNode moduleJavadoc) {
185 final DetailNode sinceTagNode = getModuleSinceVersionTagStartNode(moduleJavadoc);
186 return JavadocMetadataScraperUtil
187 .constructSubTreeText(sinceTagNode, sinceTagNode.getFirstChild())
188 .replace("@since ", "");
189 }
190
191
192
193
194
195
196
197 public static DetailNode getDescriptionEndNode(DetailNode moduleJavadoc) {
198 final DetailNode descriptionEndNode;
199
200 final DetailNode notesStartingNode =
201 getNotesSectionStartNode(moduleJavadoc);
202
203 if (notesStartingNode != null) {
204 descriptionEndNode = notesStartingNode.getPreviousSibling();
205 }
206 else {
207 descriptionEndNode = getNodeBeforeJavadocTags(moduleJavadoc);
208 }
209
210 return descriptionEndNode;
211 }
212
213
214
215
216
217
218
219 public static DetailNode getNotesSectionStartNode(DetailNode moduleJavadoc) {
220 DetailNode notesStartNode = null;
221 DetailNode node = moduleJavadoc.getFirstChild();
222
223 while (node != null) {
224 if (node.getType() == JavadocCommentsTokenTypes.HTML_ELEMENT) {
225 boolean found = false;
226 if (JavadocUtil.isTag(node, "ul")) {
227 final DetailNode htmlContentNode = JavadocUtil.findFirstToken(
228 node, JavadocCommentsTokenTypes.HTML_CONTENT);
229 if (isStartOfNotesSection(htmlContentNode.getFirstChild())) {
230 notesStartNode = node;
231 found = true;
232 }
233 }
234 else if ((JavadocUtil.isTag(node, "p")
235 || JavadocUtil.isTag(node, "li"))
236 && isStartOfNotesSection(node)) {
237 notesStartNode = node;
238 found = true;
239 }
240 if (found) {
241 break;
242 }
243 }
244 node = node.getNextSibling();
245 }
246
247 return notesStartNode;
248 }
249
250
251
252
253
254
255
256
257 public static DetailNode getModuleSinceVersionTagStartNode(DetailNode moduleJavadoc) {
258 return JavadocUtil.getAllNodesOfType(
259 moduleJavadoc, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG).stream()
260 .filter(javadocTag -> {
261 return javadocTag.getFirstChild().getType()
262 == JavadocCommentsTokenTypes.SINCE_BLOCK_TAG;
263 })
264 .findFirst()
265 .orElse(null);
266 }
267
268
269
270
271
272
273
274
275 public static DetailNode getNodeBeforeJavadocTags(DetailNode moduleJavadoc) {
276 DetailNode nodeBeforeJavadocTags = moduleJavadoc.getFirstChild();
277
278 while (nodeBeforeJavadocTags.getNextSibling() != null
279 && nodeBeforeJavadocTags.getNextSibling().getType()
280 != JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG) {
281
282 nodeBeforeJavadocTags = nodeBeforeJavadocTags.getNextSibling();
283 }
284
285 return nodeBeforeJavadocTags;
286 }
287
288
289
290
291
292
293
294 public static String getModuleNotes(DetailNode moduleJavadoc) {
295 final String result;
296
297 final DetailNode notesStartNode = getNotesSectionStartNode(moduleJavadoc);
298
299 if (notesStartNode == null) {
300 result = "";
301 }
302 else {
303 final DetailNode notesEndNode = getNodeBeforeJavadocTags(moduleJavadoc);
304
305 final String unprocessedNotes =
306 JavadocMetadataScraperUtil.constructSubTreeText(
307 notesStartNode, notesEndNode);
308 result = NOTES_LINE_WITH_NEWLINE.matcher(unprocessedNotes).replaceAll("");
309 }
310
311 return result;
312 }
313
314
315
316
317
318
319
320 public static boolean isPropertySpecialTokenProp(Field propertyField) {
321 boolean result = false;
322
323 if (propertyField != null) {
324 final XdocsPropertyType fieldXdocAnnotation =
325 propertyField.getAnnotation(XdocsPropertyType.class);
326
327 result = fieldXdocAnnotation != null
328 && fieldXdocAnnotation.value() == PropertyType.TOKEN_ARRAY;
329 }
330
331 return result;
332 }
333
334 }