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 DetailNode getDescriptionEndNode(DetailNode moduleJavadoc) {
185 final DetailNode descriptionEndNode;
186
187 final DetailNode notesStartingNode =
188 getNotesSectionStartNode(moduleJavadoc);
189
190 if (notesStartingNode != null) {
191 descriptionEndNode = notesStartingNode.getPreviousSibling();
192 }
193 else {
194 descriptionEndNode = getModuleSinceVersionTagStartNode(
195 moduleJavadoc).getPreviousSibling();
196 }
197
198 return descriptionEndNode;
199 }
200
201
202
203
204
205
206
207 public static DetailNode getNotesSectionStartNode(DetailNode moduleJavadoc) {
208 DetailNode notesStartNode = null;
209 DetailNode node = moduleJavadoc.getFirstChild();
210
211 while (node != null) {
212 if (node.getType() == JavadocCommentsTokenTypes.HTML_ELEMENT) {
213 boolean found = false;
214 if (JavadocUtil.isTag(node, "ul")) {
215 final DetailNode htmlContentNode = JavadocUtil.findFirstToken(
216 node, JavadocCommentsTokenTypes.HTML_CONTENT);
217 if (isStartOfNotesSection(htmlContentNode.getFirstChild())) {
218 notesStartNode = node;
219 found = true;
220 }
221 }
222 else if ((JavadocUtil.isTag(node, "p")
223 || JavadocUtil.isTag(node, "li"))
224 && isStartOfNotesSection(node)) {
225 notesStartNode = node;
226 found = true;
227 }
228 if (found) {
229 break;
230 }
231 }
232 node = node.getNextSibling();
233 }
234
235 return notesStartNode;
236 }
237
238
239
240
241
242
243
244
245 public static DetailNode getModuleSinceVersionTagStartNode(DetailNode moduleJavadoc) {
246 return JavadocUtil.getAllNodesOfType(
247 moduleJavadoc, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG).stream()
248 .filter(javadocTag -> {
249 return javadocTag.getFirstChild().getType()
250 == JavadocCommentsTokenTypes.SINCE_BLOCK_TAG;
251 })
252 .findFirst()
253 .orElse(null);
254 }
255
256
257
258
259
260
261
262 public static String getModuleNotes(DetailNode moduleJavadoc) {
263 final String result;
264
265 final DetailNode notesStartNode = getNotesSectionStartNode(moduleJavadoc);
266
267 if (notesStartNode == null) {
268 result = "";
269 }
270 else {
271 final DetailNode notesEndNode = getNotesEndNode(moduleJavadoc);
272
273 final String unprocessedNotes =
274 JavadocMetadataScraperUtil.constructSubTreeText(
275 notesStartNode, notesEndNode);
276 result = NOTES_LINE_WITH_NEWLINE.matcher(unprocessedNotes).replaceAll("");
277 }
278
279 return result;
280 }
281
282
283
284
285
286
287
288 public static DetailNode getNotesEndNode(DetailNode moduleJavadoc) {
289 return getModuleSinceVersionTagStartNode(
290 moduleJavadoc).getPreviousSibling();
291 }
292
293
294
295
296
297
298
299 public static boolean isPropertySpecialTokenProp(Field propertyField) {
300 boolean result = false;
301
302 if (propertyField != null) {
303 final XdocsPropertyType fieldXdocAnnotation =
304 propertyField.getAnnotation(XdocsPropertyType.class);
305
306 result = fieldXdocAnnotation != null
307 && fieldXdocAnnotation.value() == PropertyType.TOKEN_ARRAY;
308 }
309
310 return result;
311 }
312
313 }