001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.grammar; 021 022import java.util.ArrayDeque; 023import java.util.Deque; 024import java.util.Set; 025import java.util.stream.Collectors; 026 027import org.antlr.v4.runtime.Token; 028 029import com.puppycrawl.tools.checkstyle.api.JavadocCommentsTokenTypes; 030 031/** 032 * Utility class for Javadoc comments lexer operations. 033 */ 034public final class JavadocCommentsLexerUtil { 035 036 /** 037 * Private constructor to prevent instantiation of this utility class. 038 * 039 * @throws IllegalStateException if this constructor is called, indicating that 040 */ 041 private JavadocCommentsLexerUtil() { 042 throw new IllegalStateException("Utility class"); 043 } 044 045 /** 046 * Finds unclosed tag name tokens by comparing open and close tag name tokens. 047 * 048 * <p> 049 * This method attempts to match each closing tag with the most recent 050 * unmatched opening tag of the same name, considering only tags that appear 051 * before it in the token stream. Any remaining unmatched opening tags are 052 * considered unclosed and returned. 053 * </p> 054 * 055 * <p> 056 * <b>Note:</b> This method must be called after lexing is complete to ensure 057 * that all tokens have their index values set. 058 * </p> 059 * 060 * @param openTagNameTokens a deque of {@link Token} instances representing open tag names 061 * @param closeTagNameTokens a deque of {@link Token} instances representing close tag names 062 * @return a set of {@link SimpleToken} instances representing unclosed tag names 063 */ 064 public static Set<SimpleToken> getUnclosedTagNameTokens( 065 Deque<Token> openTagNameTokens, Deque<Token> closeTagNameTokens) { 066 067 final Deque<Token> unmatchedOpen = new ArrayDeque<>(openTagNameTokens); 068 069 for (Token closingTag : closeTagNameTokens) { 070 final Deque<Token> tempStack = new ArrayDeque<>(); 071 while (!unmatchedOpen.isEmpty()) { 072 final Token openingTag = unmatchedOpen.pop(); 073 if (openingTag.getText().equalsIgnoreCase(closingTag.getText()) 074 && openingTag.getTokenIndex() < closingTag.getTokenIndex()) { 075 break; 076 } 077 tempStack.push(openingTag); 078 } 079 080 // Put unmatched tags back 081 while (!tempStack.isEmpty()) { 082 unmatchedOpen.push(tempStack.pop()); 083 } 084 085 } 086 087 // We cannot map to SimpleToken until lexing has completed, otherwise 088 // the token index will not be set. 089 return unmatchedOpen.stream() 090 .map(SimpleToken::from) 091 .collect(Collectors.toUnmodifiableSet()); 092 } 093 094 /** 095 * Checks if the previous token is an open tag name. 096 * 097 * @param previousToken the previous token to check 098 * @return true if the previous token is null or not a closing tag, false otherwise 099 */ 100 public static boolean isOpenTagName(Token previousToken) { 101 return previousToken == null 102 || previousToken.getType() != JavadocCommentsTokenTypes.TAG_SLASH; 103 } 104 105}