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 }