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;
21
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Set;
25
26 import org.antlr.v4.runtime.BufferedTokenStream;
27 import org.antlr.v4.runtime.CommonTokenStream;
28 import org.antlr.v4.runtime.ParserRuleContext;
29 import org.antlr.v4.runtime.Token;
30 import org.antlr.v4.runtime.tree.ParseTree;
31 import org.antlr.v4.runtime.tree.TerminalNode;
32
33 import com.puppycrawl.tools.checkstyle.api.DetailNode;
34 import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
35 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocNodeImpl;
36 import com.puppycrawl.tools.checkstyle.grammar.javadoc.JavadocCommentsLexer;
37 import com.puppycrawl.tools.checkstyle.grammar.javadoc.JavadocCommentsParser;
38 import com.puppycrawl.tools.checkstyle.grammar.javadoc.JavadocCommentsParserBaseVisitor;
39 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class JavadocCommentsAstVisitor extends JavadocCommentsParserBaseVisitor<JavadocNodeImpl> {
60
61
62
63
64 private static final Set<Integer> JAVADOC_TAG_TYPES = Set.of(
65 JavadocCommentsLexer.CODE,
66 JavadocCommentsLexer.LINK,
67 JavadocCommentsLexer.LINKPLAIN,
68 JavadocCommentsLexer.VALUE,
69 JavadocCommentsLexer.INHERIT_DOC,
70 JavadocCommentsLexer.SUMMARY,
71 JavadocCommentsLexer.SYSTEM_PROPERTY,
72 JavadocCommentsLexer.INDEX,
73 JavadocCommentsLexer.RETURN,
74 JavadocCommentsLexer.LITERAL,
75 JavadocCommentsLexer.SNIPPET,
76 JavadocCommentsLexer.CUSTOM_NAME,
77 JavadocCommentsLexer.AUTHOR,
78 JavadocCommentsLexer.DEPRECATED,
79 JavadocCommentsLexer.PARAM,
80 JavadocCommentsLexer.THROWS,
81 JavadocCommentsLexer.EXCEPTION,
82 JavadocCommentsLexer.SINCE,
83 JavadocCommentsLexer.VERSION,
84 JavadocCommentsLexer.SEE,
85 JavadocCommentsLexer.LITERAL_HIDDEN,
86 JavadocCommentsLexer.USES,
87 JavadocCommentsLexer.PROVIDES,
88 JavadocCommentsLexer.SERIAL,
89 JavadocCommentsLexer.SERIAL_DATA,
90 JavadocCommentsLexer.SERIAL_FIELD
91 );
92
93
94
95
96 private final int blockCommentLineNumber;
97
98
99
100
101 private final int javadocColumnNumber;
102
103
104
105
106 private final BufferedTokenStream tokens;
107
108
109
110
111
112 private final Set<Integer> processedTokenIndices = new HashSet<>();
113
114
115
116
117
118 private final TextAccumulator accumulator = new TextAccumulator();
119
120
121
122
123 private DetailNode firstNonTightHtmlTag;
124
125
126
127
128
129
130
131
132 public JavadocCommentsAstVisitor(CommonTokenStream tokens,
133 int blockCommentLineNumber, int javadocColumnNumber) {
134 this.tokens = tokens;
135 this.blockCommentLineNumber = blockCommentLineNumber;
136 this.javadocColumnNumber = javadocColumnNumber;
137 }
138
139 @Override
140 public JavadocNodeImpl visitJavadoc(JavadocCommentsParser.JavadocContext ctx) {
141 return buildImaginaryNode(JavadocCommentsTokenTypes.JAVADOC_CONTENT, ctx);
142 }
143
144 @Override
145 public JavadocNodeImpl visitMainDescription(JavadocCommentsParser.MainDescriptionContext ctx) {
146 return flattenedTree(ctx);
147 }
148
149 @Override
150 public JavadocNodeImpl visitBlockTag(JavadocCommentsParser.BlockTagContext ctx) {
151 final JavadocNodeImpl blockTagNode =
152 createImaginary(JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG);
153 final ParseTree tag = ctx.getChild(0);
154 final Token tagName = (Token) tag.getChild(1).getPayload();
155 final int tokenType = tagName.getType();
156 final JavadocNodeImpl specificTagNode = switch (tokenType) {
157 case JavadocCommentsLexer.AUTHOR ->
158 buildImaginaryNode(JavadocCommentsTokenTypes.AUTHOR_BLOCK_TAG, ctx);
159 case JavadocCommentsLexer.DEPRECATED ->
160 buildImaginaryNode(JavadocCommentsTokenTypes.DEPRECATED_BLOCK_TAG, ctx);
161 case JavadocCommentsLexer.RETURN ->
162 buildImaginaryNode(JavadocCommentsTokenTypes.RETURN_BLOCK_TAG, ctx);
163 case JavadocCommentsLexer.PARAM ->
164 buildImaginaryNode(JavadocCommentsTokenTypes.PARAM_BLOCK_TAG, ctx);
165 case JavadocCommentsLexer.THROWS ->
166 buildImaginaryNode(JavadocCommentsTokenTypes.THROWS_BLOCK_TAG, ctx);
167 case JavadocCommentsLexer.EXCEPTION ->
168 buildImaginaryNode(JavadocCommentsTokenTypes.EXCEPTION_BLOCK_TAG, ctx);
169 case JavadocCommentsLexer.SINCE ->
170 buildImaginaryNode(JavadocCommentsTokenTypes.SINCE_BLOCK_TAG, ctx);
171 case JavadocCommentsLexer.VERSION ->
172 buildImaginaryNode(JavadocCommentsTokenTypes.VERSION_BLOCK_TAG, ctx);
173 case JavadocCommentsLexer.SEE ->
174 buildImaginaryNode(JavadocCommentsTokenTypes.SEE_BLOCK_TAG, ctx);
175 case JavadocCommentsLexer.LITERAL_HIDDEN ->
176 buildImaginaryNode(JavadocCommentsTokenTypes.HIDDEN_BLOCK_TAG, ctx);
177 case JavadocCommentsLexer.USES ->
178 buildImaginaryNode(JavadocCommentsTokenTypes.USES_BLOCK_TAG, ctx);
179 case JavadocCommentsLexer.PROVIDES ->
180 buildImaginaryNode(JavadocCommentsTokenTypes.PROVIDES_BLOCK_TAG, ctx);
181 case JavadocCommentsLexer.SERIAL ->
182 buildImaginaryNode(JavadocCommentsTokenTypes.SERIAL_BLOCK_TAG, ctx);
183 case JavadocCommentsLexer.SERIAL_DATA ->
184 buildImaginaryNode(JavadocCommentsTokenTypes.SERIAL_DATA_BLOCK_TAG, ctx);
185 case JavadocCommentsLexer.SERIAL_FIELD ->
186 buildImaginaryNode(JavadocCommentsTokenTypes.SERIAL_FIELD_BLOCK_TAG, ctx);
187 default ->
188 buildImaginaryNode(JavadocCommentsTokenTypes.CUSTOM_BLOCK_TAG, ctx);
189 };
190 blockTagNode.addChild(specificTagNode);
191
192 return blockTagNode;
193 }
194
195 @Override
196 public JavadocNodeImpl visitAuthorTag(JavadocCommentsParser.AuthorTagContext ctx) {
197 return flattenedTree(ctx);
198 }
199
200 @Override
201 public JavadocNodeImpl visitDeprecatedTag(JavadocCommentsParser.DeprecatedTagContext ctx) {
202 return flattenedTree(ctx);
203 }
204
205 @Override
206 public JavadocNodeImpl visitReturnTag(JavadocCommentsParser.ReturnTagContext ctx) {
207 return flattenedTree(ctx);
208 }
209
210 @Override
211 public JavadocNodeImpl visitParameterTag(JavadocCommentsParser.ParameterTagContext ctx) {
212 return flattenedTree(ctx);
213 }
214
215 @Override
216 public JavadocNodeImpl visitThrowsTag(JavadocCommentsParser.ThrowsTagContext ctx) {
217 return flattenedTree(ctx);
218 }
219
220 @Override
221 public JavadocNodeImpl visitExceptionTag(JavadocCommentsParser.ExceptionTagContext ctx) {
222 return flattenedTree(ctx);
223 }
224
225 @Override
226 public JavadocNodeImpl visitSinceTag(JavadocCommentsParser.SinceTagContext ctx) {
227 return flattenedTree(ctx);
228 }
229
230 @Override
231 public JavadocNodeImpl visitVersionTag(JavadocCommentsParser.VersionTagContext ctx) {
232 return flattenedTree(ctx);
233 }
234
235 @Override
236 public JavadocNodeImpl visitSeeTag(JavadocCommentsParser.SeeTagContext ctx) {
237 return flattenedTree(ctx);
238 }
239
240 @Override
241 public JavadocNodeImpl visitHiddenTag(JavadocCommentsParser.HiddenTagContext ctx) {
242 return flattenedTree(ctx);
243 }
244
245 @Override
246 public JavadocNodeImpl visitUsesTag(JavadocCommentsParser.UsesTagContext ctx) {
247 return flattenedTree(ctx);
248 }
249
250 @Override
251 public JavadocNodeImpl visitProvidesTag(JavadocCommentsParser.ProvidesTagContext ctx) {
252 return flattenedTree(ctx);
253 }
254
255 @Override
256 public JavadocNodeImpl visitSerialTag(JavadocCommentsParser.SerialTagContext ctx) {
257 return flattenedTree(ctx);
258 }
259
260 @Override
261 public JavadocNodeImpl visitSerialDataTag(JavadocCommentsParser.SerialDataTagContext ctx) {
262 return flattenedTree(ctx);
263 }
264
265 @Override
266 public JavadocNodeImpl visitSerialFieldTag(JavadocCommentsParser.SerialFieldTagContext ctx) {
267 return flattenedTree(ctx);
268 }
269
270 @Override
271 public JavadocNodeImpl visitCustomBlockTag(JavadocCommentsParser.CustomBlockTagContext ctx) {
272 return flattenedTree(ctx);
273 }
274
275 @Override
276 public JavadocNodeImpl visitInlineTag(JavadocCommentsParser.InlineTagContext ctx) {
277 final JavadocNodeImpl inlineTagNode =
278 createImaginary(JavadocCommentsTokenTypes.JAVADOC_INLINE_TAG);
279 final ParseTree tagContent = ctx.inlineTagContent().getChild(0);
280 final Token tagName = (Token) tagContent.getChild(0).getPayload();
281 final int tokenType = tagName.getType();
282 final JavadocNodeImpl specificTagNode = switch (tokenType) {
283 case JavadocCommentsLexer.CODE ->
284 buildImaginaryNode(JavadocCommentsTokenTypes.CODE_INLINE_TAG, ctx);
285 case JavadocCommentsLexer.LINK ->
286 buildImaginaryNode(JavadocCommentsTokenTypes.LINK_INLINE_TAG, ctx);
287 case JavadocCommentsLexer.LINKPLAIN ->
288 buildImaginaryNode(JavadocCommentsTokenTypes.LINKPLAIN_INLINE_TAG, ctx);
289 case JavadocCommentsLexer.VALUE ->
290 buildImaginaryNode(JavadocCommentsTokenTypes.VALUE_INLINE_TAG, ctx);
291 case JavadocCommentsLexer.INHERIT_DOC ->
292 buildImaginaryNode(JavadocCommentsTokenTypes.INHERIT_DOC_INLINE_TAG, ctx);
293 case JavadocCommentsLexer.SUMMARY ->
294 buildImaginaryNode(JavadocCommentsTokenTypes.SUMMARY_INLINE_TAG, ctx);
295 case JavadocCommentsLexer.SYSTEM_PROPERTY ->
296 buildImaginaryNode(JavadocCommentsTokenTypes.SYSTEM_PROPERTY_INLINE_TAG, ctx);
297 case JavadocCommentsLexer.INDEX ->
298 buildImaginaryNode(JavadocCommentsTokenTypes.INDEX_INLINE_TAG, ctx);
299 case JavadocCommentsLexer.RETURN ->
300 buildImaginaryNode(JavadocCommentsTokenTypes.RETURN_INLINE_TAG, ctx);
301 case JavadocCommentsLexer.LITERAL ->
302 buildImaginaryNode(JavadocCommentsTokenTypes.LITERAL_INLINE_TAG, ctx);
303 case JavadocCommentsLexer.SNIPPET ->
304 buildImaginaryNode(JavadocCommentsTokenTypes.SNIPPET_INLINE_TAG, ctx);
305 default -> buildImaginaryNode(JavadocCommentsTokenTypes.CUSTOM_INLINE_TAG, ctx);
306 };
307 inlineTagNode.addChild(specificTagNode);
308
309 return inlineTagNode;
310 }
311
312 @Override
313 public JavadocNodeImpl visitInlineTagContent(
314 JavadocCommentsParser.InlineTagContentContext ctx) {
315 return flattenedTree(ctx);
316 }
317
318 @Override
319 public JavadocNodeImpl visitCodeInlineTag(JavadocCommentsParser.CodeInlineTagContext ctx) {
320 return flattenedTree(ctx);
321 }
322
323 @Override
324 public JavadocNodeImpl visitLinkPlainInlineTag(
325 JavadocCommentsParser.LinkPlainInlineTagContext ctx) {
326 return flattenedTree(ctx);
327 }
328
329 @Override
330 public JavadocNodeImpl visitLinkInlineTag(JavadocCommentsParser.LinkInlineTagContext ctx) {
331 return flattenedTree(ctx);
332 }
333
334 @Override
335 public JavadocNodeImpl visitValueInlineTag(JavadocCommentsParser.ValueInlineTagContext ctx) {
336 return flattenedTree(ctx);
337 }
338
339 @Override
340 public JavadocNodeImpl visitInheritDocInlineTag(
341 JavadocCommentsParser.InheritDocInlineTagContext ctx) {
342 return flattenedTree(ctx);
343 }
344
345 @Override
346 public JavadocNodeImpl visitSummaryInlineTag(
347 JavadocCommentsParser.SummaryInlineTagContext ctx) {
348 return flattenedTree(ctx);
349 }
350
351 @Override
352 public JavadocNodeImpl visitSystemPropertyInlineTag(
353 JavadocCommentsParser.SystemPropertyInlineTagContext ctx) {
354 return flattenedTree(ctx);
355 }
356
357 @Override
358 public JavadocNodeImpl visitIndexInlineTag(JavadocCommentsParser.IndexInlineTagContext ctx) {
359 return flattenedTree(ctx);
360 }
361
362 @Override
363 public JavadocNodeImpl visitReturnInlineTag(JavadocCommentsParser.ReturnInlineTagContext ctx) {
364 return flattenedTree(ctx);
365 }
366
367 @Override
368 public JavadocNodeImpl visitLiteralInlineTag(
369 JavadocCommentsParser.LiteralInlineTagContext ctx) {
370 return flattenedTree(ctx);
371 }
372
373 @Override
374 public JavadocNodeImpl visitSnippetInlineTag(
375 JavadocCommentsParser.SnippetInlineTagContext ctx) {
376 final JavadocNodeImpl dummyRoot = new JavadocNodeImpl();
377 if (!ctx.snippetAttributes.isEmpty()) {
378 final JavadocNodeImpl snippetAttributes =
379 createImaginary(JavadocCommentsTokenTypes.SNIPPET_ATTRIBUTES);
380 ctx.snippetAttributes.forEach(snippetAttributeContext -> {
381 final JavadocNodeImpl snippetAttribute = visit(snippetAttributeContext);
382 snippetAttributes.addChild(snippetAttribute);
383 });
384 dummyRoot.addChild(snippetAttributes);
385 }
386 final TerminalNode colon = ctx.COLON();
387 if (colon != null) {
388 dummyRoot.addChild(create((Token) colon.getPayload()));
389 }
390 final JavadocCommentsParser.SnippetBodyContext snippetBody = ctx.snippetBody();
391 if (snippetBody != null) {
392 dummyRoot.addChild(visit(snippetBody));
393 }
394 return dummyRoot.getFirstChild();
395 }
396
397 @Override
398 public JavadocNodeImpl visitCustomInlineTag(JavadocCommentsParser.CustomInlineTagContext ctx) {
399 return flattenedTree(ctx);
400 }
401
402 @Override
403 public JavadocNodeImpl visitReference(JavadocCommentsParser.ReferenceContext ctx) {
404 return buildImaginaryNode(JavadocCommentsTokenTypes.REFERENCE, ctx);
405 }
406
407 @Override
408 public JavadocNodeImpl visitTypeName(JavadocCommentsParser.TypeNameContext ctx) {
409 return flattenedTree(ctx);
410
411 }
412
413 @Override
414 public JavadocNodeImpl visitQualifiedName(JavadocCommentsParser.QualifiedNameContext ctx) {
415 return flattenedTree(ctx);
416 }
417
418 @Override
419 public JavadocNodeImpl visitTypeArguments(JavadocCommentsParser.TypeArgumentsContext ctx) {
420 return buildImaginaryNode(JavadocCommentsTokenTypes.TYPE_ARGUMENTS, ctx);
421 }
422
423 @Override
424 public JavadocNodeImpl visitTypeArgument(JavadocCommentsParser.TypeArgumentContext ctx) {
425 return buildImaginaryNode(JavadocCommentsTokenTypes.TYPE_ARGUMENT, ctx);
426 }
427
428 @Override
429 public JavadocNodeImpl visitMemberReference(JavadocCommentsParser.MemberReferenceContext ctx) {
430 return buildImaginaryNode(JavadocCommentsTokenTypes.MEMBER_REFERENCE, ctx);
431 }
432
433 @Override
434 public JavadocNodeImpl visitMethodReferenceWithoutHash(
435 JavadocCommentsParser.MethodReferenceWithoutHashContext ctx) {
436 return buildImaginaryNode(JavadocCommentsTokenTypes.MEMBER_REFERENCE, ctx);
437 }
438
439 @Override
440 public JavadocNodeImpl visitParameterTypeList(
441 JavadocCommentsParser.ParameterTypeListContext ctx) {
442 return buildImaginaryNode(JavadocCommentsTokenTypes.PARAMETER_TYPE_LIST, ctx);
443 }
444
445 @Override
446 public JavadocNodeImpl visitDescription(JavadocCommentsParser.DescriptionContext ctx) {
447 return buildImaginaryNode(JavadocCommentsTokenTypes.DESCRIPTION, ctx);
448 }
449
450 @Override
451 public JavadocNodeImpl visitSnippetAttribute(
452 JavadocCommentsParser.SnippetAttributeContext ctx) {
453 return buildImaginaryNode(JavadocCommentsTokenTypes.SNIPPET_ATTRIBUTE, ctx);
454 }
455
456 @Override
457 public JavadocNodeImpl visitSnippetBody(JavadocCommentsParser.SnippetBodyContext ctx) {
458 return buildImaginaryNode(JavadocCommentsTokenTypes.SNIPPET_BODY, ctx);
459 }
460
461 @Override
462 public JavadocNodeImpl visitHtmlElement(JavadocCommentsParser.HtmlElementContext ctx) {
463 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_ELEMENT, ctx);
464 }
465
466 @Override
467 public JavadocNodeImpl visitVoidElement(JavadocCommentsParser.VoidElementContext ctx) {
468 return buildImaginaryNode(JavadocCommentsTokenTypes.VOID_ELEMENT, ctx);
469 }
470
471 @Override
472 public JavadocNodeImpl visitTightElement(JavadocCommentsParser.TightElementContext ctx) {
473 return flattenedTree(ctx);
474 }
475
476 @Override
477 public JavadocNodeImpl visitNonTightElement(JavadocCommentsParser.NonTightElementContext ctx) {
478 if (firstNonTightHtmlTag == null) {
479 final ParseTree htmlTagStart = ctx.getChild(0);
480 final ParseTree tagNameToken = htmlTagStart.getChild(1);
481 firstNonTightHtmlTag = create((Token) tagNameToken.getPayload());
482 }
483 return flattenedTree(ctx);
484 }
485
486 @Override
487 public JavadocNodeImpl visitSelfClosingElement(
488 JavadocCommentsParser.SelfClosingElementContext ctx) {
489 final JavadocNodeImpl javadocNode =
490 createImaginary(JavadocCommentsTokenTypes.VOID_ELEMENT);
491 javadocNode.addChild(create((Token) ctx.TAG_OPEN().getPayload()));
492 javadocNode.addChild(create((Token) ctx.TAG_NAME().getPayload()));
493 if (!ctx.htmlAttributes.isEmpty()) {
494 final JavadocNodeImpl htmlAttributes =
495 createImaginary(JavadocCommentsTokenTypes.HTML_ATTRIBUTES);
496 ctx.htmlAttributes.forEach(htmlAttributeContext -> {
497 final JavadocNodeImpl htmlAttribute = visit(htmlAttributeContext);
498 htmlAttributes.addChild(htmlAttribute);
499 });
500 javadocNode.addChild(htmlAttributes);
501 }
502
503 javadocNode.addChild(create((Token) ctx.TAG_SLASH_CLOSE().getPayload()));
504 return javadocNode;
505 }
506
507 @Override
508 public JavadocNodeImpl visitHtmlTagStart(JavadocCommentsParser.HtmlTagStartContext ctx) {
509 final JavadocNodeImpl javadocNode =
510 createImaginary(JavadocCommentsTokenTypes.HTML_TAG_START);
511 javadocNode.addChild(create((Token) ctx.TAG_OPEN().getPayload()));
512 javadocNode.addChild(create((Token) ctx.TAG_NAME().getPayload()));
513 if (!ctx.htmlAttributes.isEmpty()) {
514 final JavadocNodeImpl htmlAttributes =
515 createImaginary(JavadocCommentsTokenTypes.HTML_ATTRIBUTES);
516 ctx.htmlAttributes.forEach(htmlAttributeContext -> {
517 final JavadocNodeImpl htmlAttribute = visit(htmlAttributeContext);
518 htmlAttributes.addChild(htmlAttribute);
519 });
520 javadocNode.addChild(htmlAttributes);
521 }
522
523 final Token tagClose = (Token) ctx.TAG_CLOSE().getPayload();
524 addHiddenTokensToTheLeft(tagClose, javadocNode);
525 javadocNode.addChild(create(tagClose));
526 return javadocNode;
527 }
528
529 @Override
530 public JavadocNodeImpl visitHtmlTagEnd(JavadocCommentsParser.HtmlTagEndContext ctx) {
531 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_TAG_END, ctx);
532 }
533
534 @Override
535 public JavadocNodeImpl visitHtmlAttribute(JavadocCommentsParser.HtmlAttributeContext ctx) {
536 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_ATTRIBUTE, ctx);
537 }
538
539 @Override
540 public JavadocNodeImpl visitHtmlContent(JavadocCommentsParser.HtmlContentContext ctx) {
541 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_CONTENT, ctx);
542 }
543
544 @Override
545 public JavadocNodeImpl visitNonTightHtmlContent(
546 JavadocCommentsParser.NonTightHtmlContentContext ctx) {
547 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_CONTENT, ctx);
548 }
549
550 @Override
551 public JavadocNodeImpl visitHtmlComment(JavadocCommentsParser.HtmlCommentContext ctx) {
552 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_COMMENT, ctx);
553 }
554
555 @Override
556 public JavadocNodeImpl visitHtmlCommentContent(
557 JavadocCommentsParser.HtmlCommentContentContext ctx) {
558 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_COMMENT_CONTENT, ctx);
559 }
560
561
562
563
564
565
566
567
568
569 private JavadocNodeImpl buildImaginaryNode(int tokenType, ParserRuleContext ctx) {
570 final JavadocNodeImpl javadocNode = createImaginary(tokenType);
571 processChildren(javadocNode, ctx.children);
572 return javadocNode;
573 }
574
575
576
577
578
579
580
581
582 private JavadocNodeImpl flattenedTree(ParserRuleContext ctx) {
583 final JavadocNodeImpl dummyNode = new JavadocNodeImpl();
584 processChildren(dummyNode, ctx.children);
585 return dummyNode.getFirstChild();
586 }
587
588
589
590
591
592
593
594
595 private void processChildren(JavadocNodeImpl parent, List<? extends ParseTree> children) {
596 for (ParseTree child : children) {
597 if (child instanceof TerminalNode terminalNode) {
598 final Token token = (Token) terminalNode.getPayload();
599
600
601 addHiddenTokensToTheLeft(token, parent);
602
603 if (isTextToken(token)) {
604 accumulator.append(token);
605 }
606 else if (token.getType() != Token.EOF) {
607 parent.addChild(create(token));
608 }
609 }
610 else {
611 accumulator.flushTo(parent);
612 final Token token = ((ParserRuleContext) child).getStart();
613 addHiddenTokensToTheLeft(token, parent);
614 parent.addChild(visit(child));
615 }
616 }
617
618 accumulator.flushTo(parent);
619 }
620
621
622
623
624
625
626
627 private static boolean isTextToken(Token token) {
628 return token.getType() == JavadocCommentsTokenTypes.TEXT;
629 }
630
631
632
633
634
635
636
637 private static boolean isMultipleLeadingAsterisks(Token token) {
638 boolean result = false;
639
640 if (isLeadingAsterisk(token)) {
641 final String leadingAsterisks = getLeadingAsterisksText(token);
642 result = leadingAsterisks.length() > 1;
643 }
644
645 return result;
646 }
647
648
649
650
651
652
653
654 private static boolean isLeadingAsterisk(Token token) {
655 return token.getType() == JavadocCommentsLexer.LEADING_ASTERISK;
656 }
657
658
659
660
661
662
663
664 private static String getLeadingAsterisksText(Token token) {
665 return token.getText().substring(token.getText().indexOf('*'));
666 }
667
668
669
670
671
672
673
674
675
676 private void addHiddenTokensToTheLeft(Token token, JavadocNodeImpl parent) {
677 final boolean alreadyProcessed = !processedTokenIndices.add(token.getTokenIndex());
678
679 if (!alreadyProcessed) {
680 final int tokenIndex = token.getTokenIndex();
681 final List<Token> hiddenTokens = tokens.getHiddenTokensToLeft(tokenIndex);
682 if (hiddenTokens != null) {
683 accumulator.flushTo(parent);
684 for (Token hiddenToken : hiddenTokens) {
685 parent.addChild(create(hiddenToken));
686 }
687 }
688 }
689 }
690
691
692
693
694
695
696
697 private JavadocNodeImpl create(Token token) {
698 final JavadocNodeImpl node = new JavadocNodeImpl();
699 node.initialize(token);
700
701
702 node.setLineNumber(node.getLineNumber() + blockCommentLineNumber);
703
704
705 if (node.getLineNumber() == blockCommentLineNumber) {
706 node.setColumnNumber(node.getColumnNumber() + javadocColumnNumber);
707 }
708
709 final int tokenType = token.getType();
710 if (isLeadingAsterisk(token)) {
711 final String leadingAsterisks = getLeadingAsterisksText(token);
712 node.setColumnNumber(node.getColumnNumber() + token.getText().indexOf('*'));
713 node.setText(leadingAsterisks);
714 }
715 if (isJavadocTag(tokenType)) {
716 node.setType(JavadocCommentsTokenTypes.TAG_NAME);
717 }
718 if (tokenType == JavadocCommentsLexer.WS) {
719 node.setType(JavadocCommentsTokenTypes.TEXT);
720 }
721 if (isMultipleLeadingAsterisks(token)) {
722 node.setType(JavadocCommentsTokenTypes.LEADING_ASTERISKS);
723 }
724
725 return node;
726 }
727
728
729
730
731
732
733
734 private static boolean isJavadocTag(int type) {
735 return JAVADOC_TAG_TYPES.contains(type);
736 }
737
738
739
740
741
742
743
744
745
746 private JavadocNodeImpl createImaginary(int tokenType) {
747 final JavadocNodeImpl node = new JavadocNodeImpl();
748 node.setType(tokenType);
749 node.setText(JavadocUtil.getTokenName(tokenType));
750 node.setLineNumber(blockCommentLineNumber);
751 node.setColumnNumber(javadocColumnNumber);
752 return node;
753 }
754
755
756
757
758
759
760 public DetailNode getFirstNonTightHtmlTag() {
761 return firstNonTightHtmlTag;
762 }
763
764
765
766
767
768 private final class TextAccumulator {
769
770
771
772
773
774
775 private final StringBuilder buffer = new StringBuilder(256);
776
777
778
779
780 private Token startToken;
781
782
783
784
785 private TextAccumulator() {
786
787 }
788
789
790
791
792
793
794 void append(Token token) {
795 if (buffer.isEmpty()) {
796 startToken = token;
797 }
798 buffer.append(token.getText());
799 }
800
801
802
803
804
805
806
807 void flushTo(JavadocNodeImpl parent) {
808 if (!buffer.isEmpty()) {
809 final JavadocNodeImpl startNode = create(startToken);
810 startNode.setText(buffer.toString());
811 parent.addChild(startNode);
812 buffer.setLength(0);
813 }
814 }
815 }
816
817 }