Coverage Report - com.puppycrawl.tools.checkstyle.utils.JavadocUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
JavadocUtils
100%
127/127
100%
76/76
3.118
JavadocUtils$JavadocTagType
100%
4/4
N/A
3.118
 
 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.utils;
 21  
 
 22  
 import java.lang.reflect.Field;
 23  
 import java.lang.reflect.Modifier;
 24  
 import java.util.ArrayList;
 25  
 import java.util.List;
 26  
 import java.util.regex.Pattern;
 27  
 
 28  
 import com.google.common.collect.ImmutableMap;
 29  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 30  
 import com.puppycrawl.tools.checkstyle.api.DetailNode;
 31  
 import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes;
 32  
 import com.puppycrawl.tools.checkstyle.api.TextBlock;
 33  
 import com.puppycrawl.tools.checkstyle.checks.javadoc.InvalidJavadocTag;
 34  
 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTag;
 35  
 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo;
 36  
 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTags;
 37  
 import com.puppycrawl.tools.checkstyle.checks.javadoc.utils.BlockTagUtils;
 38  
 import com.puppycrawl.tools.checkstyle.checks.javadoc.utils.InlineTagUtils;
 39  
 import com.puppycrawl.tools.checkstyle.checks.javadoc.utils.TagInfo;
 40  
 
 41  
 /**
 42  
  * Contains utility methods for working with Javadoc.
 43  
  * @author Lyle Hanson
 44  
  */
 45  
 public final class JavadocUtils {
 46  
 
 47  
     /**
 48  
      * The type of Javadoc tag we want returned.
 49  
      */
 50  7
     public enum JavadocTagType {
 51  
         /** Block type. */
 52  1
         BLOCK,
 53  
         /** Inline type. */
 54  1
         INLINE,
 55  
         /** All validTags. */
 56  1
         ALL
 57  
     }
 58  
 
 59  
     /** Maps from a token name to value. */
 60  
     private static final ImmutableMap<String, Integer> TOKEN_NAME_TO_VALUE;
 61  
     /** Maps from a token value to name. */
 62  
     private static final String[] TOKEN_VALUE_TO_NAME;
 63  
 
 64  
     /** Exception message for unknown JavaDoc token id. */
 65  
     private static final String UNKNOWN_JAVADOC_TOKEN_ID_EXCEPTION_MESSAGE = "Unknown javadoc"
 66  
             + " token id. Given id: ";
 67  
 
 68  
     /** Newline pattern. */
 69  2
     private static final Pattern NEWLINE = Pattern.compile("\n");
 70  
 
 71  
     /** Return pattern. */
 72  2
     private static final Pattern RETURN = Pattern.compile("\r");
 73  
 
 74  
     /** Tab pattern. */
 75  2
     private static final Pattern TAB = Pattern.compile("\t");
 76  
 
 77  
     // Using reflection gets all token names and values from JavadocTokenTypes class
 78  
     // and saves to TOKEN_NAME_TO_VALUE and TOKEN_VALUE_TO_NAME collections.
 79  
     static {
 80  2
         final ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder();
 81  
 
 82  2
         final Field[] fields = JavadocTokenTypes.class.getDeclaredFields();
 83  
 
 84  2
         String[] tempTokenValueToName = CommonUtils.EMPTY_STRING_ARRAY;
 85  
 
 86  330
         for (final Field field : fields) {
 87  
 
 88  
             // Only process public int fields.
 89  328
             if (!Modifier.isPublic(field.getModifiers())
 90  326
                     || field.getType() != Integer.TYPE) {
 91  2
                 continue;
 92  
             }
 93  
 
 94  324
             final String name = field.getName();
 95  
 
 96  324
             final int tokenValue = TokenUtils.getIntFromField(field, name);
 97  324
             builder.put(name, tokenValue);
 98  324
             if (tokenValue > tempTokenValueToName.length - 1) {
 99  38
                 final String[] temp = new String[tokenValue + 1];
 100  38
                 System.arraycopy(tempTokenValueToName, 0, temp, 0, tempTokenValueToName.length);
 101  38
                 tempTokenValueToName = temp;
 102  
             }
 103  324
             if (tokenValue == -1) {
 104  2
                 tempTokenValueToName[0] = name;
 105  
             }
 106  
             else {
 107  322
                 tempTokenValueToName[tokenValue] = name;
 108  
             }
 109  
         }
 110  
 
 111  2
         TOKEN_NAME_TO_VALUE = builder.build();
 112  2
         TOKEN_VALUE_TO_NAME = tempTokenValueToName;
 113  2
     }
 114  
 
 115  
     /** Prevent instantiation. */
 116  1
     private JavadocUtils() {
 117  1
     }
 118  
 
 119  
     /**
 120  
      * Gets validTags from a given piece of Javadoc.
 121  
      * @param textBlock
 122  
      *        the Javadoc comment to process.
 123  
      * @param tagType
 124  
      *        the type of validTags we're interested in
 125  
      * @return all standalone validTags from the given javadoc.
 126  
      */
 127  
     public static JavadocTags getJavadocTags(TextBlock textBlock,
 128  
             JavadocTagType tagType) {
 129  
 
 130  120
         final boolean getBlockTags = tagType == JavadocTagType.ALL
 131  
                                          || tagType == JavadocTagType.BLOCK;
 132  120
         final boolean getInlineTags = tagType == JavadocTagType.ALL
 133  
                                           || tagType == JavadocTagType.INLINE;
 134  
 
 135  120
         final List<TagInfo> tags = new ArrayList<>();
 136  
 
 137  120
         if (getBlockTags) {
 138  100
             tags.addAll(BlockTagUtils.extractBlockTags(textBlock.getText()));
 139  
         }
 140  
 
 141  120
         if (getInlineTags) {
 142  26
             tags.addAll(InlineTagUtils.extractInlineTags(textBlock.getText()));
 143  
         }
 144  
 
 145  120
         final List<JavadocTag> validTags = new ArrayList<>();
 146  120
         final List<InvalidJavadocTag> invalidTags = new ArrayList<>();
 147  
 
 148  120
         for (TagInfo tag : tags) {
 149  146
             final int col = tag.getPosition().getColumn();
 150  
 
 151  
             // Add the starting line of the comment to the line number to get the actual line number
 152  
             // in the source.
 153  
             // Lines are one-indexed, so need a off-by-one correction.
 154  146
             final int line = textBlock.getStartLineNo() + tag.getPosition().getLine() - 1;
 155  
 
 156  146
             if (JavadocTagInfo.isValidName(tag.getName())) {
 157  284
                 validTags.add(
 158  142
                     new JavadocTag(line, col, tag.getName(), tag.getValue()));
 159  
             }
 160  
             else {
 161  4
                 invalidTags.add(new InvalidJavadocTag(line, col, tag.getName()));
 162  
             }
 163  146
         }
 164  
 
 165  120
         return new JavadocTags(validTags, invalidTags);
 166  
     }
 167  
 
 168  
     /**
 169  
      * Checks that commentContent starts with '*' javadoc comment identifier.
 170  
      * @param commentContent
 171  
      *        content of block comment
 172  
      * @return true if commentContent starts with '*' javadoc comment
 173  
      *         identifier.
 174  
      */
 175  
     public static boolean isJavadocComment(String commentContent) {
 176  1793
         boolean result = false;
 177  
 
 178  1793
         if (!commentContent.isEmpty()) {
 179  1791
             final char docCommentIdentificator = commentContent.charAt(0);
 180  1791
             result = docCommentIdentificator == '*';
 181  
         }
 182  
 
 183  1793
         return result;
 184  
     }
 185  
 
 186  
     /**
 187  
      * Checks block comment content starts with '*' javadoc comment identifier.
 188  
      * @param blockCommentBegin
 189  
      *        block comment AST
 190  
      * @return true if block comment content starts with '*' javadoc comment
 191  
      *         identifier.
 192  
      */
 193  
     public static boolean isJavadocComment(DetailAST blockCommentBegin) {
 194  1789
         final String commentContent = getBlockCommentContent(blockCommentBegin);
 195  1789
         return isJavadocComment(commentContent) && isCorrectJavadocPosition(blockCommentBegin);
 196  
     }
 197  
 
 198  
     /**
 199  
      * Gets content of block comment.
 200  
      * @param blockCommentBegin
 201  
      *        block comment AST.
 202  
      * @return content of block comment.
 203  
      */
 204  
     private static String getBlockCommentContent(DetailAST blockCommentBegin) {
 205  1789
         final DetailAST commentContent = blockCommentBegin.getFirstChild();
 206  1789
         return commentContent.getText();
 207  
     }
 208  
 
 209  
     /**
 210  
      * Get content of Javadoc comment.
 211  
      * @param javadocCommentBegin
 212  
      *        Javadoc comment AST
 213  
      * @return content of Javadoc comment.
 214  
      */
 215  
     public static String getJavadocCommentContent(DetailAST javadocCommentBegin) {
 216  651
         final DetailAST commentContent = javadocCommentBegin.getFirstChild();
 217  651
         return commentContent.getText().substring(1);
 218  
     }
 219  
 
 220  
     /**
 221  
      * Returns the first child token that has a specified type.
 222  
      * @param detailNode
 223  
      *        Javadoc AST node
 224  
      * @param type
 225  
      *        the token type to match
 226  
      * @return the matching token, or null if no match
 227  
      */
 228  
     public static DetailNode findFirstToken(DetailNode detailNode, int type) {
 229  64
         DetailNode returnValue = null;
 230  64
         DetailNode node = getFirstChild(detailNode);
 231  275
         while (node != null) {
 232  242
             if (node.getType() == type) {
 233  31
                 returnValue = node;
 234  31
                 break;
 235  
             }
 236  211
             node = getNextSibling(node);
 237  
         }
 238  64
         return returnValue;
 239  
     }
 240  
 
 241  
     /**
 242  
      * Gets first child node of specified node.
 243  
      *
 244  
      * @param node DetailNode
 245  
      * @return first child
 246  
      */
 247  
     public static DetailNode getFirstChild(DetailNode node) {
 248  20245
         DetailNode resultNode = null;
 249  
 
 250  20245
         if (node.getChildren().length > 0) {
 251  5232
             resultNode = node.getChildren()[0];
 252  
         }
 253  20245
         return resultNode;
 254  
     }
 255  
 
 256  
     /**
 257  
      * Checks whether node contains any node of specified type among children on any deep level.
 258  
      *
 259  
      * @param node DetailNode
 260  
      * @param type token type
 261  
      * @return true if node contains any node of type type among children on any deep level.
 262  
      */
 263  
     public static boolean containsInBranch(DetailNode node, int type) {
 264  3
         boolean result = true;
 265  3
         DetailNode curNode = node;
 266  8
         while (type != curNode.getType()) {
 267  7
             DetailNode toVisit = getFirstChild(curNode);
 268  11
             while (curNode != null && toVisit == null) {
 269  4
                 toVisit = getNextSibling(curNode);
 270  4
                 if (toVisit == null) {
 271  1
                     curNode = curNode.getParent();
 272  
                 }
 273  
             }
 274  
 
 275  7
             if (curNode == toVisit) {
 276  2
                 result = false;
 277  2
                 break;
 278  
             }
 279  
 
 280  5
             curNode = toVisit;
 281  5
         }
 282  3
         return result;
 283  
     }
 284  
 
 285  
     /**
 286  
      * Gets next sibling of specified node.
 287  
      *
 288  
      * @param node DetailNode
 289  
      * @return next sibling.
 290  
      */
 291  
     public static DetailNode getNextSibling(DetailNode node) {
 292  66132
         DetailNode nextSibling = null;
 293  66132
         final DetailNode parent = node.getParent();
 294  66132
         if (parent != null) {
 295  64883
             final int nextSiblingIndex = node.getIndex() + 1;
 296  64883
             final DetailNode[] children = parent.getChildren();
 297  64883
             if (nextSiblingIndex <= children.length - 1) {
 298  45592
                 nextSibling = children[nextSiblingIndex];
 299  
             }
 300  
         }
 301  66132
         return nextSibling;
 302  
     }
 303  
 
 304  
     /**
 305  
      * Gets next sibling of specified node with the specified type.
 306  
      *
 307  
      * @param node DetailNode
 308  
      * @param tokenType javadoc token type
 309  
      * @return next sibling.
 310  
      */
 311  
     public static DetailNode getNextSibling(DetailNode node, int tokenType) {
 312  4
         DetailNode nextSibling = getNextSibling(node);
 313  9
         while (nextSibling != null && nextSibling.getType() != tokenType) {
 314  5
             nextSibling = getNextSibling(nextSibling);
 315  
         }
 316  4
         return nextSibling;
 317  
     }
 318  
 
 319  
     /**
 320  
      * Gets previous sibling of specified node.
 321  
      * @param node DetailNode
 322  
      * @return previous sibling
 323  
      */
 324  
     public static DetailNode getPreviousSibling(DetailNode node) {
 325  1027
         DetailNode previousSibling = null;
 326  1027
         final int previousSiblingIndex = node.getIndex() - 1;
 327  1027
         if (previousSiblingIndex >= 0) {
 328  943
             final DetailNode parent = node.getParent();
 329  943
             final DetailNode[] children = parent.getChildren();
 330  943
             previousSibling = children[previousSiblingIndex];
 331  
         }
 332  1027
         return previousSibling;
 333  
     }
 334  
 
 335  
     /**
 336  
      * Returns the name of a token for a given ID.
 337  
      * @param id
 338  
      *        the ID of the token name to get
 339  
      * @return a token name
 340  
      */
 341  
     public static String getTokenName(int id) {
 342  
         final String name;
 343  7847
         if (id == JavadocTokenTypes.EOF) {
 344  58
             name = "EOF";
 345  
         }
 346  7789
         else if (id > TOKEN_VALUE_TO_NAME.length - 1) {
 347  2
             throw new IllegalArgumentException(UNKNOWN_JAVADOC_TOKEN_ID_EXCEPTION_MESSAGE + id);
 348  
         }
 349  
         else {
 350  7787
             name = TOKEN_VALUE_TO_NAME[id];
 351  7787
             if (name == null) {
 352  1
                 throw new IllegalArgumentException(UNKNOWN_JAVADOC_TOKEN_ID_EXCEPTION_MESSAGE + id);
 353  
             }
 354  
         }
 355  7844
         return name;
 356  
     }
 357  
 
 358  
     /**
 359  
      * Returns the ID of a token for a given name.
 360  
      * @param name
 361  
      *        the name of the token ID to get
 362  
      * @return a token ID
 363  
      */
 364  
     public static int getTokenId(String name) {
 365  8168
         final Integer id = TOKEN_NAME_TO_VALUE.get(name);
 366  8168
         if (id == null) {
 367  1
             throw new IllegalArgumentException("Unknown javadoc token name. Given name " + name);
 368  
         }
 369  8167
         return id;
 370  
     }
 371  
 
 372  
     /**
 373  
      * Gets tag name from javadocTagSection.
 374  
      *
 375  
      * @param javadocTagSection to get tag name from.
 376  
      * @return name, of the javadocTagSection's tag.
 377  
      */
 378  
     public static String getTagName(DetailNode javadocTagSection) {
 379  
         final String javadocTagName;
 380  18
         if (javadocTagSection.getType() == JavadocTokenTypes.JAVADOC_INLINE_TAG) {
 381  16
             javadocTagName = getNextSibling(
 382  16
                     getFirstChild(javadocTagSection)).getText();
 383  
         }
 384  
         else {
 385  10
             javadocTagName = getFirstChild(javadocTagSection).getText();
 386  
         }
 387  18
         return javadocTagName;
 388  
     }
 389  
 
 390  
     /**
 391  
      * Replace all control chars with escaped symbols.
 392  
      * @param text the String to process.
 393  
      * @return the processed String with all control chars escaped.
 394  
      */
 395  
     public static String escapeAllControlChars(String text) {
 396  7721
         final String textWithoutNewlines = NEWLINE.matcher(text).replaceAll("\\\\n");
 397  7721
         final String textWithoutReturns = RETURN.matcher(textWithoutNewlines).replaceAll("\\\\r");
 398  7721
         return TAB.matcher(textWithoutReturns).replaceAll("\\\\t");
 399  
     }
 400  
 
 401  
     /**
 402  
      * Checks Javadoc comment it's in right place.
 403  
      * From Javadoc util documentation:
 404  
      * "Placement of comments - Documentation comments are recognized only when placed
 405  
      * immediately before class, interface, constructor, method, or field
 406  
      * declarations -- see the class example, method example, and field example.
 407  
      * Documentation comments placed in the body of a method are ignored. Only one
 408  
      * documentation comment per declaration statement is recognized by the Javadoc tool."
 409  
      *
 410  
      * @param blockComment Block comment AST
 411  
      * @return true if Javadoc is in right place
 412  
      */
 413  
     private static boolean isCorrectJavadocPosition(DetailAST blockComment) {
 414  3544
         return BlockCommentPosition.isOnClass(blockComment)
 415  1695
                 || BlockCommentPosition.isOnInterface(blockComment)
 416  1673
                 || BlockCommentPosition.isOnEnum(blockComment)
 417  1651
                 || BlockCommentPosition.isOnMethod(blockComment)
 418  1344
                 || BlockCommentPosition.isOnField(blockComment)
 419  1198
                 || BlockCommentPosition.isOnConstructor(blockComment)
 420  1176
                 || BlockCommentPosition.isOnEnumConstant(blockComment)
 421  1156
                 || BlockCommentPosition.isOnAnnotationDef(blockComment);
 422  
     }
 423  
 }