Coverage Report - com.puppycrawl.tools.checkstyle.api.FileContents
 
Classes in this File Line Coverage Branch Coverage Complexity
FileContents
100%
87/87
100%
34/34
1.64
 
 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.api;
 21  
 
 22  
 import java.io.File;
 23  
 import java.util.ArrayList;
 24  
 import java.util.Arrays;
 25  
 import java.util.Collection;
 26  
 import java.util.HashMap;
 27  
 import java.util.List;
 28  
 import java.util.Map;
 29  
 import java.util.regex.Pattern;
 30  
 
 31  
 import com.google.common.collect.ImmutableMap;
 32  
 import com.puppycrawl.tools.checkstyle.grammars.CommentListener;
 33  
 import com.puppycrawl.tools.checkstyle.utils.CommonUtils;
 34  
 
 35  
 /**
 36  
  * Represents the contents of a file.
 37  
  *
 38  
  * @author Oliver Burn
 39  
  */
 40  
 public final class FileContents implements CommentListener {
 41  
     /**
 42  
      * The pattern to match a single line comment containing only the comment
 43  
      * itself -- no code.
 44  
      */
 45  
     private static final String MATCH_SINGLELINE_COMMENT_PAT = "^\\s*//.*$";
 46  
     /** Compiled regexp to match a single-line comment line. */
 47  12
     private static final Pattern MATCH_SINGLELINE_COMMENT = Pattern
 48  6
             .compile(MATCH_SINGLELINE_COMMENT_PAT);
 49  
 
 50  
     /** The file name. */
 51  
     private final String fileName;
 52  
 
 53  
     /** The text. */
 54  
     private final FileText text;
 55  
 
 56  
     /** Map of the Javadoc comments indexed on the last line of the comment.
 57  
      * The hack is it assumes that there is only one Javadoc comment per line.
 58  
      */
 59  2451
     private final Map<Integer, TextBlock> javadocComments = new HashMap<>();
 60  
     /** Map of the C++ comments indexed on the first line of the comment. */
 61  2451
     private final Map<Integer, TextBlock> cppComments = new HashMap<>();
 62  
 
 63  
     /**
 64  
      * Map of the C comments indexed on the first line of the comment to a list
 65  
      * of comments on that line.
 66  
      */
 67  2451
     private final Map<Integer, List<TextBlock>> clangComments = new HashMap<>();
 68  
 
 69  
     /**
 70  
      * Creates a new {@code FileContents} instance.
 71  
      *
 72  
      * @param filename name of the file
 73  
      * @param lines the contents of the file
 74  
      * @deprecated Use {@link #FileContents(FileText)} instead
 75  
      *     in order to preserve the original line breaks where possible.
 76  
      */
 77  
     @Deprecated
 78  6
     public FileContents(String filename, String... lines) {
 79  6
         fileName = filename;
 80  6
         text = new FileText(new File(filename), Arrays.asList(lines));
 81  6
     }
 82  
 
 83  
     /**
 84  
      * Creates a new {@code FileContents} instance.
 85  
      *
 86  
      * @param text the contents of the file
 87  
      */
 88  2445
     public FileContents(FileText text) {
 89  2445
         fileName = text.getFile().toString();
 90  2445
         this.text = new FileText(text);
 91  2445
     }
 92  
 
 93  
     @Override
 94  
     public void reportSingleLineComment(String type, int startLineNo,
 95  
             int startColNo) {
 96  51675
         reportSingleLineComment(startLineNo, startColNo);
 97  51675
     }
 98  
 
 99  
     /**
 100  
      * Report the location of a single line comment.
 101  
      * @param startLineNo the starting line number
 102  
      * @param startColNo the starting column number
 103  
      **/
 104  
     public void reportSingleLineComment(int startLineNo, int startColNo) {
 105  51685
         final String line = line(startLineNo - 1);
 106  51685
         final String[] txt = {line.substring(startColNo)};
 107  51685
         final Comment comment = new Comment(txt, startColNo, startLineNo,
 108  51685
                 line.length() - 1);
 109  51685
         cppComments.put(startLineNo, comment);
 110  51685
     }
 111  
 
 112  
     @Override
 113  
     public void reportBlockComment(String type, int startLineNo,
 114  
             int startColNo, int endLineNo, int endColNo) {
 115  141242
         reportBlockComment(startLineNo, startColNo, endLineNo, endColNo);
 116  141242
     }
 117  
 
 118  
     /**
 119  
      * Report the location of a block comment.
 120  
      * @param startLineNo the starting line number
 121  
      * @param startColNo the starting column number
 122  
      * @param endLineNo the ending line number
 123  
      * @param endColNo the ending column number
 124  
      **/
 125  
     private void reportBlockComment(int startLineNo, int startColNo,
 126  
             int endLineNo, int endColNo) {
 127  141248
         final String[] cComment = extractBlockComment(startLineNo, startColNo,
 128  
                 endLineNo, endColNo);
 129  141248
         final Comment comment = new Comment(cComment, startColNo, endLineNo,
 130  
                 endColNo);
 131  
 
 132  
         // save the comment
 133  141248
         if (clangComments.containsKey(startLineNo)) {
 134  1943
             final List<TextBlock> entries = clangComments.get(startLineNo);
 135  1943
             entries.add(comment);
 136  1943
         }
 137  
         else {
 138  139305
             final List<TextBlock> entries = new ArrayList<>();
 139  139305
             entries.add(comment);
 140  139305
             clangComments.put(startLineNo, entries);
 141  
         }
 142  
 
 143  
         // Remember if possible Javadoc comment
 144  141248
         final String firstLine = line(startLineNo - 1);
 145  141248
         if (firstLine.contains("/**") && !firstLine.contains("/**/")) {
 146  8480
             javadocComments.put(endLineNo - 1, comment);
 147  
         }
 148  141248
     }
 149  
 
 150  
     /**
 151  
      * Report the location of a C++ style comment.
 152  
      * @param startLineNo the starting line number
 153  
      * @param startColNo the starting column number
 154  
      * @deprecated Use {@link #reportSingleLineComment(int, int)} instead.
 155  
      **/
 156  
     @Deprecated
 157  
     public void reportCppComment(int startLineNo, int startColNo) {
 158  3
         reportSingleLineComment(startLineNo, startColNo);
 159  3
     }
 160  
 
 161  
     /**
 162  
      * Returns a map of all the C++ style comments. The key is a line number,
 163  
      * the value is the comment {@link TextBlock} at the line.
 164  
      * @return the Map of comments
 165  
      * @deprecated Use {@link #getSingleLineComments()} instead.
 166  
      */
 167  
     @Deprecated
 168  
     public ImmutableMap<Integer, TextBlock> getCppComments() {
 169  2
         return getSingleLineComments();
 170  
     }
 171  
 
 172  
     /**
 173  
      * Returns a map of all the single line comments. The key is a line number,
 174  
      * the value is the comment {@link TextBlock} at the line.
 175  
      * @return the Map of comments
 176  
      */
 177  
     public ImmutableMap<Integer, TextBlock> getSingleLineComments() {
 178  39
         return ImmutableMap.copyOf(cppComments);
 179  
     }
 180  
 
 181  
     /**
 182  
      * Report the location of a C-style comment.
 183  
      * @param startLineNo the starting line number
 184  
      * @param startColNo the starting column number
 185  
      * @param endLineNo the ending line number
 186  
      * @param endColNo the ending column number
 187  
      * @deprecated Use {@link #reportBlockComment(int, int, int, int)} instead.
 188  
      **/
 189  
     // -@cs[AbbreviationAsWordInName] Can't change yet since class is API.
 190  
     @Deprecated
 191  
     public void reportCComment(int startLineNo, int startColNo,
 192  
             int endLineNo, int endColNo) {
 193  6
         reportBlockComment(startLineNo, startColNo, endLineNo, endColNo);
 194  6
     }
 195  
 
 196  
     /**
 197  
      * Returns a map of all C style comments. The key is the line number, the
 198  
      * value is a {@link List} of C style comment {@link TextBlock}s
 199  
      * that start at that line.
 200  
      * @return the map of comments
 201  
      * @deprecated Use {@link #getBlockComments()} instead.
 202  
      */
 203  
     // -@cs[AbbreviationAsWordInName] Can't change yet since class is API.
 204  
     @Deprecated
 205  
     public ImmutableMap<Integer, List<TextBlock>> getCComments() {
 206  2
         return getBlockComments();
 207  
     }
 208  
 
 209  
     /**
 210  
      * Returns a map of all block comments. The key is the line number, the
 211  
      * value is a {@link List} of block comment {@link TextBlock}s
 212  
      * that start at that line.
 213  
      * @return the map of comments
 214  
      */
 215  
     public ImmutableMap<Integer, List<TextBlock>> getBlockComments() {
 216  37
         return ImmutableMap.copyOf(clangComments);
 217  
     }
 218  
 
 219  
     /**
 220  
      * Returns the specified block comment as a String array.
 221  
      * @param startLineNo the starting line number
 222  
      * @param startColNo the starting column number
 223  
      * @param endLineNo the ending line number
 224  
      * @param endColNo the ending column number
 225  
      * @return block comment as an array
 226  
      **/
 227  
     private String[] extractBlockComment(int startLineNo, int startColNo,
 228  
             int endLineNo, int endColNo) {
 229  
         final String[] returnValue;
 230  141248
         if (startLineNo == endLineNo) {
 231  137646
             returnValue = new String[1];
 232  137646
             returnValue[0] = line(startLineNo - 1).substring(startColNo,
 233  
                     endColNo + 1);
 234  
         }
 235  
         else {
 236  3602
             returnValue = new String[endLineNo - startLineNo + 1];
 237  3602
             returnValue[0] = line(startLineNo - 1).substring(startColNo);
 238  18002
             for (int i = startLineNo; i < endLineNo; i++) {
 239  14400
                 returnValue[i - startLineNo + 1] = line(i);
 240  
             }
 241  3602
             returnValue[returnValue.length - 1] = line(endLineNo - 1).substring(0,
 242  
                     endColNo + 1);
 243  
         }
 244  141248
         return returnValue;
 245  
     }
 246  
 
 247  
     /**
 248  
      * Returns the Javadoc comment before the specified line.
 249  
      * A return value of {@code null} means there is no such comment.
 250  
      * @param lineNoBefore the line number to check before
 251  
      * @return the Javadoc comment, or {@code null} if none
 252  
      **/
 253  
     public TextBlock getJavadocBefore(int lineNoBefore) {
 254  
         // Lines start at 1 to the callers perspective, so need to take off 2
 255  1398
         int lineNo = lineNoBefore - 2;
 256  
 
 257  
         // skip blank lines
 258  1697
         while (lineNo > 0 && (lineIsBlank(lineNo) || lineIsComment(lineNo))) {
 259  299
             lineNo--;
 260  
         }
 261  
 
 262  1398
         return javadocComments.get(lineNo);
 263  
     }
 264  
 
 265  
     /**
 266  
      * Get a single line.
 267  
      * For internal use only, as getText().get(lineNo) is just as
 268  
      * suitable for external use and avoids method duplication.
 269  
      * @param lineNo the number of the line to get
 270  
      * @return the corresponding line, without terminator
 271  
      * @throws IndexOutOfBoundsException if lineNo is invalid
 272  
      */
 273  
     private String line(int lineNo) {
 274  355617
         return text.get(lineNo);
 275  
     }
 276  
 
 277  
     /**
 278  
      * Get the full text of the file.
 279  
      * @return an object containing the full text of the file
 280  
      */
 281  
     public FileText getText() {
 282  2486
         return new FileText(text);
 283  
     }
 284  
 
 285  
     /**
 286  
      * Gets the lines in the file.
 287  
      * @return the lines in the file
 288  
      */
 289  
     public String[] getLines() {
 290  7534
         return text.toLinesArray();
 291  
     }
 292  
 
 293  
     /**
 294  
      * Get the line from text of the file.
 295  
      * @param index index of the line
 296  
      * @return line from text of the file
 297  
      */
 298  
     public String getLine(int index) {
 299  62563
         return text.get(index);
 300  
     }
 301  
 
 302  
     /**
 303  
      * Gets the name of the file.
 304  
      * @return the name of the file
 305  
      */
 306  
     public String getFileName() {
 307  2458
         return fileName;
 308  
     }
 309  
 
 310  
     /**
 311  
      * Checks if the specified line is blank.
 312  
      * @param lineNo the line number to check
 313  
      * @return if the specified line consists only of tabs and spaces.
 314  
      **/
 315  
     public boolean lineIsBlank(int lineNo) {
 316  1919
         return CommonUtils.isBlank(line(lineNo));
 317  
     }
 318  
 
 319  
     /**
 320  
      * Checks if the specified line is a single-line comment without code.
 321  
      * @param lineNo  the line number to check
 322  
      * @return if the specified line consists of only a single line comment
 323  
      *         without code.
 324  
      **/
 325  
     public boolean lineIsComment(int lineNo) {
 326  1515
         return MATCH_SINGLELINE_COMMENT.matcher(line(lineNo)).matches();
 327  
     }
 328  
 
 329  
     /**
 330  
      * Checks if the specified position intersects with a comment.
 331  
      * @param startLineNo the starting line number
 332  
      * @param startColNo the starting column number
 333  
      * @param endLineNo the ending line number
 334  
      * @param endColNo the ending column number
 335  
      * @return true if the positions intersects with a comment.
 336  
      **/
 337  
     public boolean hasIntersectionWithComment(int startLineNo,
 338  
             int startColNo, int endLineNo, int endColNo) {
 339  172
         return hasIntersectionWithBlockComment(startLineNo, startColNo, endLineNo, endColNo)
 340  45
                 || hasIntersectionWithSingleLineComment(startLineNo, startColNo, endLineNo,
 341  
                         endColNo);
 342  
     }
 343  
 
 344  
     /**
 345  
      * Checks if the current file is a package-info.java file.
 346  
      * @return true if the package file.
 347  
      */
 348  
     public boolean inPackageInfo() {
 349  136
         return fileName.endsWith("package-info.java");
 350  
     }
 351  
 
 352  
     /**
 353  
      * Checks if the specified position intersects with a block comment.
 354  
      * @param startLineNo the starting line number
 355  
      * @param startColNo the starting column number
 356  
      * @param endLineNo the ending line number
 357  
      * @param endColNo the ending column number
 358  
      * @return true if the positions intersects with a block comment.
 359  
      */
 360  
     private boolean hasIntersectionWithBlockComment(int startLineNo, int startColNo,
 361  
             int endLineNo, int endColNo) {
 362  86
         boolean hasIntersection = false;
 363  
         // Check C comments (all comments should be checked)
 364  86
         final Collection<List<TextBlock>> values = clangComments.values();
 365  86
         for (final List<TextBlock> row : values) {
 366  1383
             for (final TextBlock comment : row) {
 367  1409
                 if (comment.intersects(startLineNo, startColNo, endLineNo, endColNo)) {
 368  41
                     hasIntersection = true;
 369  41
                     break;
 370  
                 }
 371  1368
             }
 372  1383
             if (hasIntersection) {
 373  41
                 break;
 374  
             }
 375  1342
         }
 376  86
         return hasIntersection;
 377  
     }
 378  
 
 379  
     /**
 380  
      * Checks if the specified position intersects with a single line comment.
 381  
      * @param startLineNo the starting line number
 382  
      * @param startColNo the starting column number
 383  
      * @param endLineNo the ending line number
 384  
      * @param endColNo the ending column number
 385  
      * @return true if the positions intersects with a single line comment.
 386  
      */
 387  
     private boolean hasIntersectionWithSingleLineComment(int startLineNo, int startColNo,
 388  
             int endLineNo, int endColNo) {
 389  45
         boolean hasIntersection = false;
 390  
         // Check CPP comments (line searching is possible)
 391  45
         for (int lineNumber = startLineNo; lineNumber <= endLineNo;
 392  10
              lineNumber++) {
 393  49
             final TextBlock comment = cppComments.get(lineNumber);
 394  49
             if (comment != null && comment.intersects(startLineNo, startColNo,
 395  
                     endLineNo, endColNo)) {
 396  39
                 hasIntersection = true;
 397  39
                 break;
 398  
             }
 399  
         }
 400  45
         return hasIntersection;
 401  
     }
 402  
 }