JavadocCommentsLexerUtil.java
///////////////////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
// Copyright (C) 2001-2025 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
///////////////////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.grammar;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Set;
import java.util.stream.Collectors;
import org.antlr.v4.runtime.Token;
import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes;
/**
* Utility class for Javadoc comments lexer operations.
*/
public final class JavadocCommentsLexerUtil {
/**
* Private constructor to prevent instantiation of this utility class.
*
* @throws IllegalStateException if this constructor is called, indicating that
*/
private JavadocCommentsLexerUtil() {
throw new IllegalStateException("Utility class");
}
/**
* Finds unclosed tag name tokens by comparing open and close tag name tokens.
*
* <p>
* This method attempts to match each closing tag with the most recent
* unmatched opening tag of the same name, considering only tags that appear
* before it in the token stream. Any remaining unmatched opening tags are
* considered unclosed and returned.
* </p>
*
* <p>
* <b>Note:</b> This method must be called after lexing is complete to ensure
* that all tokens have their index values set.
* </p>
*
* @param openTagNameTokens a deque of {@link Token} instances representing open tag names
* @param closeTagNameTokens a deque of {@link Token} instances representing close tag names
* @return a set of {@link SimpleToken} instances representing unclosed tag names
*/
public static Set<SimpleToken> getUnclosedTagNameTokens(
Deque<Token> openTagNameTokens, Deque<Token> closeTagNameTokens) {
final Deque<Token> unmatchedOpen = new ArrayDeque<>(openTagNameTokens);
for (Token closingTag : closeTagNameTokens) {
final Deque<Token> tempStack = new ArrayDeque<>();
while (!unmatchedOpen.isEmpty()) {
final Token openingTag = unmatchedOpen.pop();
if (openingTag.getText().equalsIgnoreCase(closingTag.getText())
&& openingTag.getTokenIndex() < closingTag.getTokenIndex()) {
break;
}
tempStack.push(openingTag);
}
// Put unmatched tags back
while (!tempStack.isEmpty()) {
unmatchedOpen.push(tempStack.pop());
}
}
// We cannot map to SimpleToken until lexing has completed, otherwise
// the token index will not be set.
return unmatchedOpen.stream()
.map(SimpleToken::from)
.collect(Collectors.toUnmodifiableSet());
}
/**
* Checks if the previous token is an open tag name.
*
* @param previousToken the previous token to check
* @return true if the previous token is null or not a closing tag, false otherwise
*/
public static boolean isOpenTagName(Token previousToken) {
return previousToken == null
|| previousToken.getType() != JavadocCommentsTokenTypes.TAG_SLASH;
}
}