Coverage Report - com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractJavadocCheck
100%
93/93
100%
44/44
0
AbstractJavadocCheck$1
N/A
N/A
0
AbstractJavadocCheck$FileContext
100%
2/2
N/A
0
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2018 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.checks.javadoc;
 21  
 
 22  
 import java.util.Arrays;
 23  
 import java.util.HashMap;
 24  
 import java.util.HashSet;
 25  
 import java.util.Locale;
 26  
 import java.util.Map;
 27  
 import java.util.Set;
 28  
 
 29  
 import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser;
 30  
 import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseErrorMessage;
 31  
 import com.puppycrawl.tools.checkstyle.JavadocDetailNodeParser.ParseStatus;
 32  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 33  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 34  
 import com.puppycrawl.tools.checkstyle.api.DetailNode;
 35  
 import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
 36  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 37  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 38  
 import com.puppycrawl.tools.checkstyle.utils.JavadocUtils;
 39  
 
 40  
 /**
 41  
  * Base class for Checks that process Javadoc comments.
 42  
  * @author Baratali Izmailov
 43  
  * @noinspection NoopMethodInAbstractClass
 44  
  */
 45  110
 public abstract class AbstractJavadocCheck extends AbstractCheck {
 46  
 
 47  
     /**
 48  
      * Message key of error message. Missed close HTML tag breaks structure
 49  
      * of parse tree, so parser stops parsing and generates such error
 50  
      * message. This case is special because parser prints error like
 51  
      * {@code "no viable alternative at input 'b \n *\n'"} and it is not
 52  
      * clear that error is about missed close HTML tag.
 53  
      */
 54  
     public static final String MSG_JAVADOC_MISSED_HTML_CLOSE =
 55  
             JavadocDetailNodeParser.MSG_JAVADOC_MISSED_HTML_CLOSE;
 56  
 
 57  
     /**
 58  
      * Message key of error message.
 59  
      */
 60  
     public static final String MSG_JAVADOC_WRONG_SINGLETON_TAG =
 61  
             JavadocDetailNodeParser.MSG_JAVADOC_WRONG_SINGLETON_TAG;
 62  
 
 63  
     /**
 64  
      * Parse error while rule recognition.
 65  
      */
 66  
     public static final String MSG_JAVADOC_PARSE_RULE_ERROR =
 67  
             JavadocDetailNodeParser.MSG_JAVADOC_PARSE_RULE_ERROR;
 68  
 
 69  
     /**
 70  
      * Key is "line:column". Value is {@link DetailNode} tree. Map is stored in {@link ThreadLocal}
 71  
      * to guarantee basic thread safety and avoid shared, mutable state when not necessary.
 72  
      */
 73  4
     private static final ThreadLocal<Map<String, ParseStatus>> TREE_CACHE =
 74  2
             ThreadLocal.withInitial(HashMap::new);
 75  
 
 76  
     /**
 77  
      * The file context.
 78  
      * @noinspection ThreadLocalNotStaticFinal
 79  
      */
 80  147
     private final ThreadLocal<FileContext> context = ThreadLocal.withInitial(FileContext::new);
 81  
 
 82  
     /** The javadoc tokens the check is interested in. */
 83  110
     private final Set<Integer> javadocTokens = new HashSet<>();
 84  
 
 85  
     /**
 86  
      * This property determines if a check should log a violation upon encountering javadoc with
 87  
      * non-tight html. The default return value for this method is set to false since checks
 88  
      * generally tend to be fine with non tight html. It can be set through config file if a check
 89  
      * is to log violation upon encountering non-tight HTML in javadoc.
 90  
      *
 91  
      * @see ParseStatus#firstNonTightHtmlTag
 92  
      * @see ParseStatus#isNonTight()
 93  
      * @see <a href="http://checkstyle.sourceforge.net/writingjavadocchecks.html#Tight-HTML_rules">
 94  
      *     Tight HTML rules</a>
 95  
      */
 96  
     private boolean violateExecutionOnNonTightHtml;
 97  
 
 98  
     /**
 99  
      * Returns the default javadoc token types a check is interested in.
 100  
      * @return the default javadoc token types
 101  
      * @see JavadocTokenTypes
 102  
      */
 103  
     public abstract int[] getDefaultJavadocTokens();
 104  
 
 105  
     /**
 106  
      * Called to process a Javadoc token.
 107  
      * @param ast
 108  
      *        the token to process
 109  
      */
 110  
     public abstract void visitJavadocToken(DetailNode ast);
 111  
 
 112  
     /**
 113  
      * The configurable javadoc token set.
 114  
      * Used to protect Checks against malicious users who specify an
 115  
      * unacceptable javadoc token set in the configuration file.
 116  
      * The default implementation returns the check's default javadoc tokens.
 117  
      * @return the javadoc token set this check is designed for.
 118  
      * @see JavadocTokenTypes
 119  
      */
 120  
     public int[] getAcceptableJavadocTokens() {
 121  93
         final int[] defaultJavadocTokens = getDefaultJavadocTokens();
 122  93
         final int[] copy = new int[defaultJavadocTokens.length];
 123  93
         System.arraycopy(defaultJavadocTokens, 0, copy, 0, defaultJavadocTokens.length);
 124  93
         return copy;
 125  
     }
 126  
 
 127  
     /**
 128  
      * The javadoc tokens that this check must be registered for.
 129  
      * @return the javadoc token set this must be registered for.
 130  
      * @see JavadocTokenTypes
 131  
      */
 132  
     public int[] getRequiredJavadocTokens() {
 133  24
         return CommonUtils.EMPTY_INT_ARRAY;
 134  
     }
 135  
 
 136  
     /**
 137  
      * This method determines if a check should process javadoc containing non-tight html tags.
 138  
      * This method must be overridden in checks extending {@code AbstractJavadocCheck} which
 139  
      * are not supposed to process javadoc containing non-tight html tags.
 140  
      *
 141  
      * @return true if the check should or can process javadoc containing non-tight html tags;
 142  
      *     false otherwise
 143  
      * @see ParseStatus#isNonTight()
 144  
      * @see <a href="http://checkstyle.sourceforge.net/writingjavadocchecks.html#Tight-HTML_rules">
 145  
      *     Tight HTML rules</a>
 146  
      */
 147  
     public boolean acceptJavadocWithNonTightHtml() {
 148  527
         return true;
 149  
     }
 150  
 
 151  
     /**
 152  
      * Setter for {@link #violateExecutionOnNonTightHtml}.
 153  
      * @param shouldReportViolation value to which the field shall be set to
 154  
      * @see <a href="http://checkstyle.sourceforge.net/writingjavadocchecks.html#Tight-HTML_rules">
 155  
      *     Tight HTML rules</a>
 156  
      */
 157  
     public final void setViolateExecutionOnNonTightHtml(boolean shouldReportViolation) {
 158  3
         violateExecutionOnNonTightHtml = shouldReportViolation;
 159  3
     }
 160  
 
 161  
     /**
 162  
      * Adds a set of tokens the check is interested in.
 163  
      * @param strRep the string representation of the tokens interested in
 164  
      */
 165  
     public final void setJavadocTokens(String... strRep) {
 166  2
         javadocTokens.clear();
 167  4
         for (String str : strRep) {
 168  2
             javadocTokens.add(JavadocUtils.getTokenId(str));
 169  
         }
 170  2
     }
 171  
 
 172  
     @Override
 173  
     public void init() {
 174  64
         validateDefaultJavadocTokens();
 175  63
         if (javadocTokens.isEmpty()) {
 176  146
             for (int id : getDefaultJavadocTokens()) {
 177  85
                 javadocTokens.add(id);
 178  
             }
 179  
         }
 180  
         else {
 181  2
             final int[] acceptableJavadocTokens = getAcceptableJavadocTokens();
 182  2
             Arrays.sort(acceptableJavadocTokens);
 183  2
             for (Integer javadocTokenId : javadocTokens) {
 184  2
                 if (Arrays.binarySearch(acceptableJavadocTokens, javadocTokenId) < 0) {
 185  2
                     final String message = String.format(Locale.ROOT, "Javadoc Token \"%s\" was "
 186  
                             + "not found in Acceptable javadoc tokens list in check %s",
 187  1
                             JavadocUtils.getTokenName(javadocTokenId), getClass().getName());
 188  1
                     throw new IllegalStateException(message);
 189  
                 }
 190  1
             }
 191  
         }
 192  62
     }
 193  
 
 194  
     /**
 195  
      * Validates that check's required javadoc tokens are subset of default javadoc tokens.
 196  
      * @throws IllegalStateException when validation of default javadoc tokens fails
 197  
      */
 198  
     private void validateDefaultJavadocTokens() {
 199  64
         if (getRequiredJavadocTokens().length != 0) {
 200  44
             final int[] defaultJavadocTokens = getDefaultJavadocTokens();
 201  44
             Arrays.sort(defaultJavadocTokens);
 202  96
             for (final int javadocToken : getRequiredJavadocTokens()) {
 203  53
                 if (Arrays.binarySearch(defaultJavadocTokens, javadocToken) < 0) {
 204  2
                     final String message = String.format(Locale.ROOT,
 205  
                             "Javadoc Token \"%s\" from required javadoc "
 206  
                                 + "tokens was not found in default "
 207  
                                 + "javadoc tokens list in check %s",
 208  1
                             javadocToken, getClass().getName());
 209  1
                     throw new IllegalStateException(message);
 210  
                 }
 211  
             }
 212  
         }
 213  63
     }
 214  
 
 215  
     /**
 216  
      * Called before the starting to process a tree.
 217  
      * @param rootAst
 218  
      *        the root of the tree
 219  
      * @noinspection WeakerAccess
 220  
      */
 221  
     public void beginJavadocTree(DetailNode rootAst) {
 222  
         // No code by default, should be overridden only by demand at subclasses
 223  548
     }
 224  
 
 225  
     /**
 226  
      * Called after finished processing a tree.
 227  
      * @param rootAst
 228  
      *        the root of the tree
 229  
      * @noinspection WeakerAccess
 230  
      */
 231  
     public void finishJavadocTree(DetailNode rootAst) {
 232  
         // No code by default, should be overridden only by demand at subclasses
 233  548
     }
 234  
 
 235  
     /**
 236  
      * Called after all the child nodes have been process.
 237  
      * @param ast
 238  
      *        the token leaving
 239  
      */
 240  
     public void leaveJavadocToken(DetailNode ast) {
 241  
         // No code by default, should be overridden only by demand at subclasses
 242  884
     }
 243  
 
 244  
     /**
 245  
      * Defined final to not allow JavadocChecks to change default tokens.
 246  
      * @return default tokens
 247  
      */
 248  
     @Override
 249  
     public final int[] getDefaultTokens() {
 250  157
         return getRequiredTokens();
 251  
     }
 252  
 
 253  
     @Override
 254  
     public final int[] getAcceptableTokens() {
 255  34
         return getRequiredTokens();
 256  
     }
 257  
 
 258  
     @Override
 259  
     public final int[] getRequiredTokens() {
 260  352
         return new int[] {TokenTypes.BLOCK_COMMENT_BEGIN };
 261  
     }
 262  
 
 263  
     /**
 264  
      * Defined final because all JavadocChecks require comment nodes.
 265  
      * @return true
 266  
      */
 267  
     @Override
 268  
     public final boolean isCommentNodesRequired() {
 269  124
         return true;
 270  
     }
 271  
 
 272  
     @Override
 273  
     public final void beginTree(DetailAST rootAST) {
 274  51
         TREE_CACHE.get().clear();
 275  51
     }
 276  
 
 277  
     @Override
 278  
     public final void finishTree(DetailAST rootAST) {
 279  51
         TREE_CACHE.get().clear();
 280  51
     }
 281  
 
 282  
     @Override
 283  
     public final void visitToken(DetailAST blockCommentNode) {
 284  1860
         if (JavadocUtils.isJavadocComment(blockCommentNode)) {
 285  
             // store as field, to share with child Checks
 286  613
             context.get().blockCommentAst = blockCommentNode;
 287  
 
 288  613
             final String treeCacheKey = blockCommentNode.getLineNo() + ":"
 289  613
                     + blockCommentNode.getColumnNo();
 290  
 
 291  
             final ParseStatus result;
 292  
 
 293  613
             if (TREE_CACHE.get().containsKey(treeCacheKey)) {
 294  9
                 result = TREE_CACHE.get().get(treeCacheKey);
 295  
             }
 296  
             else {
 297  604
                 result = context.get().parser
 298  604
                         .parseJavadocAsDetailNode(blockCommentNode);
 299  604
                 TREE_CACHE.get().put(treeCacheKey, result);
 300  
             }
 301  
 
 302  613
             if (result.getParseErrorMessage() == null) {
 303  578
                 if (acceptJavadocWithNonTightHtml() || !result.isNonTight()) {
 304  548
                     processTree(result.getTree());
 305  
                 }
 306  
 
 307  578
                 if (violateExecutionOnNonTightHtml && result.isNonTight()) {
 308  60
                     log(result.getFirstNonTightHtmlTag().getLine(),
 309  
                             JavadocDetailNodeParser.MSG_UNCLOSED_HTML_TAG,
 310  30
                             result.getFirstNonTightHtmlTag().getText());
 311  
                 }
 312  
             }
 313  
             else {
 314  35
                 final ParseErrorMessage parseErrorMessage = result.getParseErrorMessage();
 315  70
                 log(parseErrorMessage.getLineNumber(),
 316  35
                         parseErrorMessage.getMessageKey(),
 317  35
                         parseErrorMessage.getMessageArguments());
 318  
             }
 319  
         }
 320  1860
     }
 321  
 
 322  
     /**
 323  
      * Getter for block comment in Java language syntax tree.
 324  
      * @return A block comment in the syntax tree.
 325  
      */
 326  
     protected DetailAST getBlockCommentAst() {
 327  123
         return context.get().blockCommentAst;
 328  
     }
 329  
 
 330  
     /**
 331  
      * Processes JavadocAST tree notifying Check.
 332  
      * @param root
 333  
      *        root of JavadocAST tree.
 334  
      */
 335  
     private void processTree(DetailNode root) {
 336  548
         beginJavadocTree(root);
 337  548
         walk(root);
 338  548
         finishJavadocTree(root);
 339  548
     }
 340  
 
 341  
     /**
 342  
      * Processes a node calling Check at interested nodes.
 343  
      * @param root
 344  
      *        the root of tree for process
 345  
      */
 346  
     private void walk(DetailNode root) {
 347  548
         DetailNode curNode = root;
 348  12775
         while (curNode != null) {
 349  12227
             boolean waitsForProcessing = shouldBeProcessed(curNode);
 350  
 
 351  12227
             if (waitsForProcessing) {
 352  949
                 visitJavadocToken(curNode);
 353  
             }
 354  12227
             DetailNode toVisit = JavadocUtils.getFirstChild(curNode);
 355  24454
             while (curNode != null && toVisit == null) {
 356  12227
                 if (waitsForProcessing) {
 357  949
                     leaveJavadocToken(curNode);
 358  
                 }
 359  
 
 360  12227
                 toVisit = JavadocUtils.getNextSibling(curNode);
 361  12227
                 if (toVisit == null) {
 362  3277
                     curNode = curNode.getParent();
 363  3277
                     if (curNode != null) {
 364  2729
                         waitsForProcessing = shouldBeProcessed(curNode);
 365  
                     }
 366  
                 }
 367  
             }
 368  12227
             curNode = toVisit;
 369  12227
         }
 370  548
     }
 371  
 
 372  
     /**
 373  
      * Checks whether the current node should be processed by the check.
 374  
      * @param curNode current node.
 375  
      * @return true if the current node should be processed by the check.
 376  
      */
 377  
     private boolean shouldBeProcessed(DetailNode curNode) {
 378  14956
         return javadocTokens.contains(curNode.getType());
 379  
     }
 380  
 
 381  
     /**
 382  
      * The file context holder.
 383  
      */
 384  1414
     private static class FileContext {
 385  
 
 386  
         /**
 387  
          * Parses content of Javadoc comment as DetailNode tree.
 388  
          */
 389  37
         private final JavadocDetailNodeParser parser = new JavadocDetailNodeParser();
 390  
 
 391  
         /**
 392  
          * DetailAST node of considered Javadoc comment that is just a block comment
 393  
          * in Java language syntax tree.
 394  
          */
 395  
         private DetailAST blockCommentAst;
 396  
 
 397  
     }
 398  
 
 399  
 }