1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.grammar;
21
22 import java.util.ArrayDeque;
23 import java.util.Deque;
24 import java.util.Set;
25 import java.util.stream.Collectors;
26
27 import org.antlr.v4.runtime.Token;
28
29 import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
30
31 /**
32 * Utility class for Javadoc comments lexer operations.
33 */
34 public final class JavadocCommentsLexerUtil {
35
36 /**
37 * Private constructor to prevent instantiation of this utility class.
38 *
39 * @throws IllegalStateException if this constructor is called, indicating that
40 */
41 private JavadocCommentsLexerUtil() {
42 throw new IllegalStateException("Utility class");
43 }
44
45 /**
46 * Finds unclosed tag name tokens by comparing open and close tag name tokens.
47 *
48 * <p>
49 * This method attempts to match each closing tag with the most recent
50 * unmatched opening tag of the same name, considering only tags that appear
51 * before it in the token stream. Any remaining unmatched opening tags are
52 * considered unclosed and returned.
53 * </p>
54 *
55 * <p>
56 * <b>Note:</b> This method must be called after lexing is complete to ensure
57 * that all tokens have their index values set.
58 * </p>
59 *
60 * @param openTagNameTokens a deque of {@link Token} instances representing open tag names
61 * @param closeTagNameTokens a deque of {@link Token} instances representing close tag names
62 * @return a set of {@link SimpleToken} instances representing unclosed tag names
63 */
64 public static Set<SimpleToken> getUnclosedTagNameTokens(
65 Deque<Token> openTagNameTokens, Deque<Token> closeTagNameTokens) {
66
67 final Deque<Token> unmatchedOpen = new ArrayDeque<>(openTagNameTokens);
68
69 for (Token closingTag : closeTagNameTokens) {
70 final Deque<Token> tempStack = new ArrayDeque<>();
71 while (!unmatchedOpen.isEmpty()) {
72 final Token openingTag = unmatchedOpen.pop();
73 if (openingTag.getText().equalsIgnoreCase(closingTag.getText())
74 && openingTag.getTokenIndex() < closingTag.getTokenIndex()) {
75 break;
76 }
77 tempStack.push(openingTag);
78 }
79
80 // Put unmatched tags back
81 while (!tempStack.isEmpty()) {
82 unmatchedOpen.push(tempStack.pop());
83 }
84
85 }
86
87 // We cannot map to SimpleToken until lexing has completed, otherwise
88 // the token index will not be set.
89 return unmatchedOpen.stream()
90 .map(SimpleToken::from)
91 .collect(Collectors.toUnmodifiableSet());
92 }
93
94 /**
95 * Checks if the previous token is an open tag name.
96 *
97 * @param previousToken the previous token to check
98 * @return true if the previous token is null or not a closing tag, false otherwise
99 */
100 public static boolean isOpenTagName(Token previousToken) {
101 return previousToken == null
102 || previousToken.getType() != JavadocCommentsTokenTypes.TAG_SLASH;
103 }
104
105 }