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_8 = SiteUtil.getNewlineAndIndentSpaces(8);
44
45 public static final String INDENT_LEVEL_10 = SiteUtil.getNewlineAndIndentSpaces(10);
46
47 public static final String INDENT_LEVEL_12 = SiteUtil.getNewlineAndIndentSpaces(12);
48
49 public static final String INDENT_LEVEL_14 = SiteUtil.getNewlineAndIndentSpaces(14);
50
51 public static final String INDENT_LEVEL_16 = SiteUtil.getNewlineAndIndentSpaces(16);
52
53 public static final String INDENT_LEVEL_18 = SiteUtil.getNewlineAndIndentSpaces(18);
54
55 public static final String INDENT_LEVEL_20 = SiteUtil.getNewlineAndIndentSpaces(20);
56
57 public static final Set<String> HTML_TEXT_FORMAT_TAGS = Set.of("<code>", "<a", "</a>", "<b>",
58 "</b>", "<strong>", "</strong>", "<i>", "</i>", "<em>", "</em>", "<small>", "</small>",
59 "<ins>", "<sub>", "<sup>");
60
61 public static final String NOTES = "Notes:";
62
63 public static final Pattern NOTES_LINE = Pattern.compile("\\s*" + NOTES + "$");
64
65 public static final Pattern NOTES_LINE_WITH_NEWLINE = Pattern.compile("\r?\n\\s?" + NOTES);
66
67
68
69
70 private ModuleJavadocParsingUtil() {
71 }
72
73
74
75
76
77
78
79
80 public static Set<String> getPropertyNames(String moduleName)
81 throws MacroExecutionException {
82 final Object instance = SiteUtil.getModuleInstance(moduleName);
83 final Class<?> clss = instance.getClass();
84
85 return SiteUtil.getPropertiesForDocumentation(clss, instance);
86 }
87
88
89
90
91
92
93
94 private static boolean isStartOfNotesSection(DetailNode htmlElement) {
95 final DetailNode htmlContentNode = JavadocUtil.findFirstToken(
96 htmlElement, JavadocCommentsTokenTypes.HTML_CONTENT);
97
98 return htmlContentNode != null && JavadocMetadataScraperUtil.isChildNodeTextMatches(
99 htmlContentNode, NOTES_LINE);
100 }
101
102
103
104
105
106
107
108 public static void writeOutJavadocPortion(String javadocPortion, Sink sink) {
109 final String[] javadocPortionLinesSplit = javadocPortion.split(NEWLINE
110 .replace("\r", ""));
111
112 sink.rawText(javadocPortionLinesSplit[0]);
113 String lastHtmlTag = javadocPortionLinesSplit[0];
114
115 for (int index = 1; index < javadocPortionLinesSplit.length; index++) {
116 final String currentLine = javadocPortionLinesSplit[index].trim();
117 final String processedLine;
118
119 if (currentLine.isEmpty()) {
120 processedLine = NEWLINE;
121 }
122 else if (currentLine.startsWith("<")
123 && !startsWithTextFormattingHtmlTag(currentLine)) {
124
125 processedLine = INDENT_LEVEL_8 + currentLine;
126 lastHtmlTag = currentLine;
127 }
128 else if (lastHtmlTag.contains("<pre")) {
129 final String currentLineWithPreservedIndent = javadocPortionLinesSplit[index]
130 .substring(1);
131
132 processedLine = NEWLINE + currentLineWithPreservedIndent;
133 }
134 else {
135 processedLine = INDENT_LEVEL_10 + currentLine;
136 }
137
138 sink.rawText(processedLine);
139 }
140
141 }
142
143
144
145
146
147
148
149 public static boolean startsWithTextFormattingHtmlTag(String line) {
150 boolean result = false;
151
152 for (String tag : HTML_TEXT_FORMAT_TAGS) {
153 if (line.startsWith(tag)) {
154 result = true;
155 break;
156 }
157 }
158
159 return result;
160 }
161
162
163
164
165
166
167
168 public static String getModuleDescription(DetailNode moduleJavadoc) {
169 final DetailNode descriptionEndNode = getDescriptionEndNode(moduleJavadoc);
170
171 return JavadocMetadataScraperUtil.constructSubTreeText(moduleJavadoc, descriptionEndNode);
172 }
173
174
175
176
177
178
179
180 public static DetailNode getDescriptionEndNode(DetailNode moduleJavadoc) {
181 final DetailNode descriptionEndNode;
182
183 final DetailNode notesStartingNode =
184 getNotesSectionStartNode(moduleJavadoc);
185
186 if (notesStartingNode != null) {
187 descriptionEndNode = notesStartingNode.getPreviousSibling();
188 }
189 else {
190 descriptionEndNode = getModuleSinceVersionTagStartNode(
191 moduleJavadoc).getPreviousSibling();
192 }
193
194 return descriptionEndNode;
195 }
196
197
198
199
200
201
202
203 public static DetailNode getNotesSectionStartNode(DetailNode moduleJavadoc) {
204 DetailNode notesStartNode = null;
205 DetailNode node = moduleJavadoc.getFirstChild();
206
207 while (node != null) {
208 if (node.getType() == JavadocCommentsTokenTypes.HTML_ELEMENT) {
209 boolean found = false;
210 if (JavadocUtil.isTag(node, "ul")) {
211 final DetailNode htmlContentNode = JavadocUtil.findFirstToken(
212 node, JavadocCommentsTokenTypes.HTML_CONTENT);
213 if (isStartOfNotesSection(htmlContentNode.getFirstChild())) {
214 notesStartNode = node;
215 found = true;
216 }
217 }
218 else if ((JavadocUtil.isTag(node, "p")
219 || JavadocUtil.isTag(node, "li"))
220 && isStartOfNotesSection(node)) {
221 notesStartNode = node;
222 found = true;
223 }
224 if (found) {
225 break;
226 }
227 }
228 node = node.getNextSibling();
229 }
230
231 return notesStartNode;
232 }
233
234
235
236
237
238
239
240
241 public static DetailNode getModuleSinceVersionTagStartNode(DetailNode moduleJavadoc) {
242 return JavadocUtil.getAllNodesOfType(
243 moduleJavadoc, JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG).stream()
244 .filter(javadocTag -> {
245 return javadocTag.getFirstChild().getType()
246 == JavadocCommentsTokenTypes.SINCE_BLOCK_TAG;
247 })
248 .findFirst()
249 .orElse(null);
250 }
251
252
253
254
255
256
257
258 public static String getModuleNotes(DetailNode moduleJavadoc) {
259 final String result;
260
261 final DetailNode notesStartNode = getNotesSectionStartNode(moduleJavadoc);
262
263 if (notesStartNode == null) {
264 result = "";
265 }
266 else {
267 final DetailNode notesEndNode = getNotesEndNode(moduleJavadoc);
268
269 final String unprocessedNotes =
270 JavadocMetadataScraperUtil.constructSubTreeText(
271 notesStartNode, notesEndNode);
272 result = NOTES_LINE_WITH_NEWLINE.matcher(unprocessedNotes).replaceAll("");
273 }
274
275 return result;
276 }
277
278
279
280
281
282
283
284 public static DetailNode getNotesEndNode(DetailNode moduleJavadoc) {
285 return getModuleSinceVersionTagStartNode(
286 moduleJavadoc).getPreviousSibling();
287 }
288
289
290
291
292
293
294
295 public static boolean isPropertySpecialTokenProp(Field propertyField) {
296 boolean result = false;
297
298 if (propertyField != null) {
299 final XdocsPropertyType fieldXdocAnnotation =
300 propertyField.getAnnotation(XdocsPropertyType.class);
301
302 result = fieldXdocAnnotation != null
303 && fieldXdocAnnotation.value() == PropertyType.TOKEN_ARRAY;
304 }
305
306 return result;
307 }
308
309 }