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