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 visitParameterTypeList(
435 JavadocCommentsParser.ParameterTypeListContext ctx) {
436 return buildImaginaryNode(JavadocCommentsTokenTypes.PARAMETER_TYPE_LIST, ctx);
437 }
438
439 @Override
440 public JavadocNodeImpl visitDescription(JavadocCommentsParser.DescriptionContext ctx) {
441 return buildImaginaryNode(JavadocCommentsTokenTypes.DESCRIPTION, ctx);
442 }
443
444 @Override
445 public JavadocNodeImpl visitSnippetAttribute(
446 JavadocCommentsParser.SnippetAttributeContext ctx) {
447 return buildImaginaryNode(JavadocCommentsTokenTypes.SNIPPET_ATTRIBUTE, ctx);
448 }
449
450 @Override
451 public JavadocNodeImpl visitSnippetBody(JavadocCommentsParser.SnippetBodyContext ctx) {
452 return buildImaginaryNode(JavadocCommentsTokenTypes.SNIPPET_BODY, ctx);
453 }
454
455 @Override
456 public JavadocNodeImpl visitHtmlElement(JavadocCommentsParser.HtmlElementContext ctx) {
457 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_ELEMENT, ctx);
458 }
459
460 @Override
461 public JavadocNodeImpl visitVoidElement(JavadocCommentsParser.VoidElementContext ctx) {
462 return buildImaginaryNode(JavadocCommentsTokenTypes.VOID_ELEMENT, ctx);
463 }
464
465 @Override
466 public JavadocNodeImpl visitTightElement(JavadocCommentsParser.TightElementContext ctx) {
467 return flattenedTree(ctx);
468 }
469
470 @Override
471 public JavadocNodeImpl visitNonTightElement(JavadocCommentsParser.NonTightElementContext ctx) {
472 if (firstNonTightHtmlTag == null) {
473 final ParseTree htmlTagStart = ctx.getChild(0);
474 final ParseTree tagNameToken = htmlTagStart.getChild(1);
475 firstNonTightHtmlTag = create((Token) tagNameToken.getPayload());
476 }
477 return flattenedTree(ctx);
478 }
479
480 @Override
481 public JavadocNodeImpl visitSelfClosingElement(
482 JavadocCommentsParser.SelfClosingElementContext ctx) {
483 final JavadocNodeImpl javadocNode =
484 createImaginary(JavadocCommentsTokenTypes.VOID_ELEMENT);
485 javadocNode.addChild(create((Token) ctx.TAG_OPEN().getPayload()));
486 javadocNode.addChild(create((Token) ctx.TAG_NAME().getPayload()));
487 if (!ctx.htmlAttributes.isEmpty()) {
488 final JavadocNodeImpl htmlAttributes =
489 createImaginary(JavadocCommentsTokenTypes.HTML_ATTRIBUTES);
490 ctx.htmlAttributes.forEach(htmlAttributeContext -> {
491 final JavadocNodeImpl htmlAttribute = visit(htmlAttributeContext);
492 htmlAttributes.addChild(htmlAttribute);
493 });
494 javadocNode.addChild(htmlAttributes);
495 }
496
497 javadocNode.addChild(create((Token) ctx.TAG_SLASH_CLOSE().getPayload()));
498 return javadocNode;
499 }
500
501 @Override
502 public JavadocNodeImpl visitHtmlTagStart(JavadocCommentsParser.HtmlTagStartContext ctx) {
503 final JavadocNodeImpl javadocNode =
504 createImaginary(JavadocCommentsTokenTypes.HTML_TAG_START);
505 javadocNode.addChild(create((Token) ctx.TAG_OPEN().getPayload()));
506 javadocNode.addChild(create((Token) ctx.TAG_NAME().getPayload()));
507 if (!ctx.htmlAttributes.isEmpty()) {
508 final JavadocNodeImpl htmlAttributes =
509 createImaginary(JavadocCommentsTokenTypes.HTML_ATTRIBUTES);
510 ctx.htmlAttributes.forEach(htmlAttributeContext -> {
511 final JavadocNodeImpl htmlAttribute = visit(htmlAttributeContext);
512 htmlAttributes.addChild(htmlAttribute);
513 });
514 javadocNode.addChild(htmlAttributes);
515 }
516
517 final Token tagClose = (Token) ctx.TAG_CLOSE().getPayload();
518 addHiddenTokensToTheLeft(tagClose, javadocNode);
519 javadocNode.addChild(create(tagClose));
520 return javadocNode;
521 }
522
523 @Override
524 public JavadocNodeImpl visitHtmlTagEnd(JavadocCommentsParser.HtmlTagEndContext ctx) {
525 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_TAG_END, ctx);
526 }
527
528 @Override
529 public JavadocNodeImpl visitHtmlAttribute(JavadocCommentsParser.HtmlAttributeContext ctx) {
530 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_ATTRIBUTE, ctx);
531 }
532
533 @Override
534 public JavadocNodeImpl visitHtmlContent(JavadocCommentsParser.HtmlContentContext ctx) {
535 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_CONTENT, ctx);
536 }
537
538 @Override
539 public JavadocNodeImpl visitNonTightHtmlContent(
540 JavadocCommentsParser.NonTightHtmlContentContext ctx) {
541 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_CONTENT, ctx);
542 }
543
544 @Override
545 public JavadocNodeImpl visitHtmlComment(JavadocCommentsParser.HtmlCommentContext ctx) {
546 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_COMMENT, ctx);
547 }
548
549 @Override
550 public JavadocNodeImpl visitHtmlCommentContent(
551 JavadocCommentsParser.HtmlCommentContentContext ctx) {
552 return buildImaginaryNode(JavadocCommentsTokenTypes.HTML_COMMENT_CONTENT, ctx);
553 }
554
555
556
557
558
559
560
561
562
563 private JavadocNodeImpl buildImaginaryNode(int tokenType, ParserRuleContext ctx) {
564 final JavadocNodeImpl javadocNode = createImaginary(tokenType);
565 processChildren(javadocNode, ctx.children);
566 return javadocNode;
567 }
568
569
570
571
572
573
574
575
576 private JavadocNodeImpl flattenedTree(ParserRuleContext ctx) {
577 final JavadocNodeImpl dummyNode = new JavadocNodeImpl();
578 processChildren(dummyNode, ctx.children);
579 return dummyNode.getFirstChild();
580 }
581
582
583
584
585
586
587
588
589 private void processChildren(JavadocNodeImpl parent, List<? extends ParseTree> children) {
590 for (ParseTree child : children) {
591 if (child instanceof TerminalNode terminalNode) {
592 final Token token = (Token) terminalNode.getPayload();
593
594
595 addHiddenTokensToTheLeft(token, parent);
596
597 if (isTextToken(token)) {
598 accumulator.append(token);
599 }
600 else if (token.getType() != Token.EOF) {
601 parent.addChild(create(token));
602 }
603 }
604 else {
605 accumulator.flushTo(parent);
606 final Token token = ((ParserRuleContext) child).getStart();
607 addHiddenTokensToTheLeft(token, parent);
608 parent.addChild(visit(child));
609 }
610 }
611
612 accumulator.flushTo(parent);
613 }
614
615
616
617
618
619
620
621 private static boolean isTextToken(Token token) {
622 return token.getType() == JavadocCommentsTokenTypes.TEXT;
623 }
624
625
626
627
628
629
630
631
632
633 private void addHiddenTokensToTheLeft(Token token, JavadocNodeImpl parent) {
634 final boolean alreadyProcessed = !processedTokenIndices.add(token.getTokenIndex());
635
636 if (!alreadyProcessed) {
637 final int tokenIndex = token.getTokenIndex();
638 final List<Token> hiddenTokens = tokens.getHiddenTokensToLeft(tokenIndex);
639 if (hiddenTokens != null) {
640 accumulator.flushTo(parent);
641 for (Token hiddenToken : hiddenTokens) {
642 parent.addChild(create(hiddenToken));
643 }
644 }
645 }
646 }
647
648
649
650
651
652
653
654 private JavadocNodeImpl create(Token token) {
655 final JavadocNodeImpl node = new JavadocNodeImpl();
656 node.initialize(token);
657
658
659 node.setLineNumber(node.getLineNumber() + blockCommentLineNumber);
660
661
662 if (node.getLineNumber() == blockCommentLineNumber) {
663 node.setColumnNumber(node.getColumnNumber() + javadocColumnNumber);
664 }
665
666 final int tokenType = token.getType();
667 if (isJavadocTag(tokenType)) {
668 node.setType(JavadocCommentsTokenTypes.TAG_NAME);
669 }
670 if (tokenType == JavadocCommentsLexer.WS) {
671 node.setType(JavadocCommentsTokenTypes.TEXT);
672 }
673
674 return node;
675 }
676
677
678
679
680
681
682
683 private static boolean isJavadocTag(int type) {
684 return JAVADOC_TAG_TYPES.contains(type);
685 }
686
687
688
689
690
691
692
693
694
695 private JavadocNodeImpl createImaginary(int tokenType) {
696 final JavadocNodeImpl node = new JavadocNodeImpl();
697 node.setType(tokenType);
698 node.setText(JavadocUtil.getTokenName(tokenType));
699 node.setLineNumber(blockCommentLineNumber);
700 node.setColumnNumber(javadocColumnNumber);
701 return node;
702 }
703
704
705
706
707
708
709 public DetailNode getFirstNonTightHtmlTag() {
710 return firstNonTightHtmlTag;
711 }
712
713
714
715
716
717 private final class TextAccumulator {
718
719
720
721
722
723
724 private final StringBuilder buffer = new StringBuilder(256);
725
726
727
728
729 private Token startToken;
730
731
732
733
734
735
736 void append(Token token) {
737 if (buffer.isEmpty()) {
738 startToken = token;
739 }
740 buffer.append(token.getText());
741 }
742
743
744
745
746
747
748
749 void flushTo(JavadocNodeImpl parent) {
750 if (!buffer.isEmpty()) {
751 final JavadocNodeImpl startNode = create(startToken);
752 startNode.setText(buffer.toString());
753 parent.addChild(startNode);
754 buffer.setLength(0);
755 }
756 }
757 }
758 }