<commit_before>////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2015 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.checks.javadoc;

import java.util.HashMap;
import java.util.Map;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;

import com.google.common.base.CaseFormat;
import com.google.common.primitives.Ints;
import com.puppycrawl.tools.checkstyle.api.Check;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.DetailNode;
import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.grammars.javadoc.JavadocLexer;
import com.puppycrawl.tools.checkstyle.grammars.javadoc.JavadocParser;
import com.puppycrawl.tools.checkstyle.utils.JavadocUtils;

/**
 * Base class for Checks that process Javadoc comments.
 * @author <NAME>
 */
public abstract class AbstractJavadocCheck extends Check {
    /**
     * Error message key for common javadoc errors.
     */
    public static final String PARSE_ERROR_MESSAGE_KEY = "javadoc.parse.error";

    /**
     * Unrecognized error from antlr parser.
     */
    public static final String UNRECOGNIZED_ANTLR_ERROR_MESSAGE_KEY =
            "javadoc.unrecognized.antlr.error";
    /**
     * Message key of error message. Missed close HTML tag breaks structure
     * of parse tree, so parser stops parsing and generates such error
     * message. This case is special because parser prints error like
     * {@code "no viable alternative at input 'b \n *\n'"} and it is not
     * clear that error is about missed close HTML tag.
     */
    static final String JAVADOC_MISSED_HTML_CLOSE = "javadoc.missed.html.close";
    /**
     * Message key of error message.
     */
    static final String JAVADOC_WRONG_SINGLETON_TAG =
        "javadoc.wrong.singleton.html.tag";

    /**
     * Key is "line:column".
     * value is DetailNode tree.
     */
    private static final Map<String, ParseStatus> TREE_CACHE = new HashMap<>();

    /**
     * Custom error listener.
     */
    private final DescriptiveErrorListener errorListener =
            new DescriptiveErrorListener();

    /**
     * DetailAST node of considered Javadoc comment that is just a block comment
     * in Java language syntax tree.
     */
    private DetailAST blockCommentAst;

    /**
     * Returns the default token types a check is interested in.
     * @return the default token types
     * @see JavadocTokenTypes
     */
    public abstract int[] getDefaultJavadocTokens();

    /**
     * Called before the starting to process a tree.
     * @param rootAst
     *        the root of the tree
     */
    public void beginJavadocTree(DetailNode rootAst) {
        // No code by default, should be overridden only by demand at subclasses
    }

    /**
     * Called after finished processing a tree.
     * @param rootAst
     *        the root of the tree
     */
    public void finishJavadocTree(DetailNode rootAst) {
        // No code by default, should be overridden only by demand at subclasses
    }

    /**
     * Called to process a Javadoc token.
     * @param ast
     *        the token to process
     */
    public abstract void visitJavadocToken(DetailNode ast);

    /**
     * Called after all the child nodes have been process.
     * @param ast
     *        the token leaving
     */
    public void leaveJavadocToken(DetailNode ast) {
        // No code by default, should be overridden only by demand at subclasses
    }

    /**
     * Defined final to not allow JavadocChecks to change default tokens.
     * @return default tokens
     */
    @Override
    public final int[] getDefaultTokens() {
        return new int[] {TokenTypes.BLOCK_COMMENT_BEGIN };
    }

    /**
     * Defined final because all JavadocChecks require comment nodes.
     * @return true
     */
    @Override
    public final boolean isCommentNodesRequired() {
        return true;
    }

    @Override
    public final void beginTree(DetailAST rootAST) {
        TREE_CACHE.clear();
    }

    @Override
    public final void finishTree(DetailAST rootAST) {
        TREE_CACHE.clear();
    }

    @Override
    public final void visitToken(DetailAST blockCommentNode) {
        if (JavadocUtils.isJavadocComment(blockCommentNode)) {
            blockCommentAst = blockCommentNode;

            final String treeCacheKey = blockCommentNode.getLineNo() + ":"
                    + blockCommentNode.getColumnNo();

            ParseStatus ps;

            if (TREE_CACHE.containsKey(treeCacheKey)) {
                ps = TREE_CACHE.get(treeCacheKey);
            }
            else {
                ps = parseJavadocAsDetailNode(blockCommentNode);
                TREE_CACHE.put(treeCacheKey, ps);
            }

            if (ps.getParseErrorMessage() == null) {
                processTree(ps.getTree());
            }
            else {<commit_msg>Issue #2237: Add basic thread-safety to Javadoc checks<commit_after>////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2015 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.checks.javadoc;

import java.util.HashMap;
import java.util.Map;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.BailErrorStrategy;
import org.antlr.v4.runtime.BaseErrorListener;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Recognizer;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;

import com.google.common.base.CaseFormat;
import com.google.common.primitives.Ints;
import com.puppycrawl.tools.checkstyle.api.Check;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.DetailNode;
import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
import com.puppycrawl.tools.checkstyle.grammars.javadoc.JavadocLexer;
import com.puppycrawl.tools.checkstyle.grammars.javadoc.JavadocParser;
import com.puppycrawl.tools.checkstyle.utils.JavadocUtils;

/**
 * Base class for Checks that process Javadoc comments.
 * @author <NAME>
 */
public abstract class AbstractJavadocCheck extends Check {
    /**
     * Error message key for common javadoc errors.
     */
    public static final String PARSE_ERROR_MESSAGE_KEY = "javadoc.parse.error";

    /**
     * Unrecognized error from antlr parser.
     */
    public static final String UNRECOGNIZED_ANTLR_ERROR_MESSAGE_KEY =
            "javadoc.unrecognized.antlr.error";
    /**
     * Message key of error message. Missed close HTML tag breaks structure
     * of parse tree, so parser stops parsing and generates such error
     * message. This case is special because parser prints error like
     * {@code "no viable alternative at input 'b \n *\n'"} and it is not
     * clear that error is about missed close HTML tag.
     */
    static final String JAVADOC_MISSED_HTML_CLOSE = "javadoc.missed.html.close";
    /**
     * Message key of error message.
     */
    static final String JAVADOC_WRONG_SINGLETON_TAG =
        "javadoc.wrong.singleton.html.tag";

    /**
     * Key is "line:column". Value is {@link DetailNode} tree. Map is stored in {@link ThreadLocal}
     * to guarantee basic thread safety and avoid shared, mutable state when not necessary.
     */
    private static final ThreadLocal<Map<String, ParseStatus>> TREE_CACHE =
        new ThreadLocal<Map<String, ParseStatus>>() {
            @Override
            protected Map<String, ParseStatus> initialValue() {
                return new HashMap<>();
            }
        };

    /**
     * Custom error listener.
     */
    private final DescriptiveErrorListener errorListener =
            new DescriptiveErrorListener();

    /**
     * DetailAST node of considered Javadoc comment that is just a block comment
     * in Java language syntax tree.
     */
    private DetailAST blockCommentAst;

    /**
     * Returns the default token types a check is interested in.
     * @return the default token types
     * @see JavadocTokenTypes
     */
    public abstract int[] getDefaultJavadocTokens();

    /**
     * Called before the starting to process a tree.
     * @param rootAst
     *        the root of the tree
     */
    public void beginJavadocTree(DetailNode rootAst) {
        // No code by default, should be overridden only by demand at subclasses
    }

    /**
     * Called after finished processing a tree.
     * @param rootAst
     *        the root of the tree
     */
    public void finishJavadocTree(DetailNode rootAst) {
        // No code by default, should be overridden only by demand at subclasses
    }

    /**
     * Called to process a Javadoc token.
     * @param ast
     *        the token to process
     */
    public abstract void visitJavadocToken(DetailNode ast);

    /**
     * Called after all the child nodes have been process.
     * @param ast
     *        the token leaving
     */
    public void leaveJavadocToken(DetailNode ast) {
        // No code by default, should be overridden only by demand at subclasses
    }

    /**
     * Defined final to not allow JavadocChecks to change default tokens.
     * @return default tokens
     */
    @Override
    public final int[] getDefaultTokens() {
        return new int[] {TokenTypes.BLOCK_COMMENT_BEGIN };
    }

    /**
     * Defined final because all JavadocChecks require comment nodes.
     * @return true
     */
    @Override
    public final boolean isCommentNodesRequired() {
        return true;
    }

    @Override
    public final void beginTree(DetailAST rootAST) {
        TREE_CACHE.get().clear();
    }

    @Override
    public final void finishTree(DetailAST rootAST) {
        TREE_CACHE.get().clear();
    }

    @Override
    public final void visitToken(DetailAST blockCommentNode) {
        if (JavadocUtils.isJavadocComment(blockCommentNode)) {
            blockCommentAst = blockCommentNode;

            final String treeCacheKey = blockCommentNode.getLineNo() + ":"
                    + blockCommentNode.getColumnNo();

            ParseStatus ps;

            if (TREE_CACHE.get().containsKey(treeCacheKey)) {
                ps = TREE_CACHE.get().get(treeCacheKey);
            }
            else {
                ps = parseJavadocAsDetailNode(blockCommentNode);
                TREE_CACHE.get().put(treeCacheKey, ps);
            }

            if (ps.getParseErrorMessage() == null) {
                processTree(ps.getTree());
            }
            else {
{'fraction_non_alphanumeric': 0.10689234522720342, 'fraction_numerical': 0.0056419640134187255, 'mean_word_length': 2.496002132196162, 'pattern_counts': {'":': 2, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6724621', 'n_tokens_mistral': 3777, 'n_tokens_neox': 3564, 'n_words': 1136}