Coverage Report - com.puppycrawl.tools.checkstyle.api.FileContents
 
Classes in this File Line Coverage Branch Coverage Complexity
FileContents
93%
62/66
96%
25/26
1.833
 
 1  
 ////////////////////////////////////////////////////////////////////////////////
 2  
 // checkstyle: Checks Java source code for adherence to a set of rules.
 3  
 // Copyright (C) 2001-2014  Oliver Burn
 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  
 package com.puppycrawl.tools.checkstyle.api;
 20  
 
 21  
 import com.google.common.collect.ImmutableMap;
 22  
 
 23  
 import com.google.common.collect.Lists;
 24  
 import com.google.common.collect.Maps;
 25  
 import com.puppycrawl.tools.checkstyle.grammars.CommentListener;
 26  
 import java.io.File;
 27  
 import java.util.Arrays;
 28  
 import java.util.Collection;
 29  
 import java.util.List;
 30  
 import java.util.Map;
 31  
 import java.util.regex.Pattern;
 32  
 
 33  
 /**
 34  
  * Represents the contents of a file.
 35  
  *
 36  
  * @author Oliver Burn
 37  
  * @version 1.0
 38  
  */
 39  
 public final class FileContents implements CommentListener
 40  
 {
 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  1
     private static final Pattern MATCH_SINGLELINE_COMMENT = Pattern
 48  
             .compile(MATCH_SINGLELINE_COMMENT_PAT);
 49  
 
 50  
     /** the file name */
 51  
     private final String mFilename;
 52  
 
 53  
     /** the text */
 54  
     private final FileText mText;
 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  749
     private final Map<Integer, TextBlock> mJavadocComments = Maps.newHashMap();
 60  
     /** map of the C++ comments indexed on the first line of the comment. */
 61  749
     private final Map<Integer, TextBlock> mCPlusPlusComments =
 62  
         Maps.newHashMap();
 63  
 
 64  
     /**
 65  
      * map of the C comments indexed on the first line of the comment to a list
 66  
      * of comments on that line
 67  
      */
 68  749
     private final Map<Integer, List<TextBlock>> mCComments = Maps.newHashMap();
 69  
 
 70  
     /**
 71  
      * Creates a new <code>FileContents</code> instance.
 72  
      *
 73  
      * @param aFilename name of the file
 74  
      * @param aLines the contents of the file
 75  
      * @deprecated Use {@link #FileContents(FileText)} instead
 76  
      *   in order to preserve the original line breaks where possible.
 77  
      */
 78  
     @Deprecated public FileContents(String aFilename, String[] aLines)
 79  0
     {
 80  0
         mFilename = aFilename;
 81  0
         mText = FileText.fromLines(new File(aFilename), Arrays.asList(aLines));
 82  0
     }
 83  
 
 84  
     /**
 85  
      * Creates a new <code>FileContents</code> instance.
 86  
      *
 87  
      * @param aText the contents of the file
 88  
      */
 89  
     public FileContents(FileText aText)
 90  749
     {
 91  749
         mFilename = aText.getFile().toString();
 92  749
         mText = aText;
 93  749
     }
 94  
 
 95  
     /** {@inheritDoc} */
 96  
     public void reportSingleLineComment(String aType, int aStartLineNo,
 97  
             int aStartColNo)
 98  
     {
 99  5987
         reportCppComment(aStartLineNo, aStartColNo);
 100  5987
     }
 101  
 
 102  
     /** {@inheritDoc} */
 103  
     public void reportBlockComment(String aType, int aStartLineNo,
 104  
             int aStartColNo, int aEndLineNo, int aEndColNo)
 105  
     {
 106  4087
         reportCComment(aStartLineNo, aStartColNo, aEndLineNo, aEndColNo);
 107  4087
     }
 108  
 
 109  
     /**
 110  
      * Report the location of a C++ style comment.
 111  
      * @param aStartLineNo the starting line number
 112  
      * @param aStartColNo the starting column number
 113  
      **/
 114  
     public void reportCppComment(int aStartLineNo, int aStartColNo)
 115  
     {
 116  5987
         final String line = line(aStartLineNo - 1);
 117  5987
         final String[] txt = new String[] {line.substring(aStartColNo)};
 118  5987
         final Comment comment = new Comment(txt, aStartColNo, aStartLineNo,
 119  
                 line.length() - 1);
 120  5987
         mCPlusPlusComments.put(aStartLineNo, comment);
 121  5987
     }
 122  
 
 123  
     /**
 124  
      * Returns a map of all the C++ style comments. The key is a line number,
 125  
      * the value is the comment {@link TextBlock} at the line.
 126  
      * @return the Map of comments
 127  
      */
 128  
     public ImmutableMap<Integer, TextBlock> getCppComments()
 129  
     {
 130  16
         return ImmutableMap.copyOf(mCPlusPlusComments);
 131  
     }
 132  
 
 133  
     /**
 134  
      * Report the location of a C-style comment.
 135  
      * @param aStartLineNo the starting line number
 136  
      * @param aStartColNo the starting column number
 137  
      * @param aEndLineNo the ending line number
 138  
      * @param aEndColNo the ending column number
 139  
      **/
 140  
     public void reportCComment(int aStartLineNo, int aStartColNo,
 141  
             int aEndLineNo, int aEndColNo)
 142  
     {
 143  4087
         final String[] cc = extractCComment(aStartLineNo, aStartColNo,
 144  
                 aEndLineNo, aEndColNo);
 145  4087
         final Comment comment = new Comment(cc, aStartColNo, aEndLineNo,
 146  
                 aEndColNo);
 147  
 
 148  
         // save the comment
 149  4087
         if (mCComments.containsKey(aStartLineNo)) {
 150  102
             final List<TextBlock> entries = mCComments.get(aStartLineNo);
 151  102
             entries.add(comment);
 152  102
         }
 153  
         else {
 154  3985
             final List<TextBlock> entries = Lists.newArrayList();
 155  3985
             entries.add(comment);
 156  3985
             mCComments.put(aStartLineNo, entries);
 157  
         }
 158  
 
 159  
         // Remember if possible Javadoc comment
 160  4087
         if (line(aStartLineNo - 1).indexOf("/**", aStartColNo) != -1) {
 161  3330
             mJavadocComments.put(aEndLineNo - 1, comment);
 162  
         }
 163  4087
     }
 164  
 
 165  
     /**
 166  
      * Returns a map of all C style comments. The key is the line number, the
 167  
      * value is a {@link List} of C style comment {@link TextBlock}s
 168  
      * that start at that line.
 169  
      * @return the map of comments
 170  
      */
 171  
     public ImmutableMap<Integer, List<TextBlock>> getCComments()
 172  
     {
 173  16
         return ImmutableMap.copyOf(mCComments);
 174  
     }
 175  
 
 176  
     /**
 177  
      * Returns the specified C comment as a String array.
 178  
      * @return C comment as a array
 179  
      * @param aStartLineNo the starting line number
 180  
      * @param aStartColNo the starting column number
 181  
      * @param aEndLineNo the ending line number
 182  
      * @param aEndColNo the ending column number
 183  
      **/
 184  
     private String[] extractCComment(int aStartLineNo, int aStartColNo,
 185  
             int aEndLineNo, int aEndColNo)
 186  
     {
 187  
         String[] retVal;
 188  4087
         if (aStartLineNo == aEndLineNo) {
 189  2393
             retVal = new String[1];
 190  2393
             retVal[0] = line(aStartLineNo - 1).substring(aStartColNo,
 191  
                     aEndColNo + 1);
 192  
         }
 193  
         else {
 194  1694
             retVal = new String[aEndLineNo - aStartLineNo + 1];
 195  1694
             retVal[0] = line(aStartLineNo - 1).substring(aStartColNo);
 196  7833
             for (int i = aStartLineNo; i < aEndLineNo; i++) {
 197  6139
                 retVal[i - aStartLineNo + 1] = line(i);
 198  
             }
 199  1694
             retVal[retVal.length - 1] = line(aEndLineNo - 1).substring(0,
 200  
                     aEndColNo + 1);
 201  
         }
 202  4087
         return retVal;
 203  
     }
 204  
 
 205  
     /**
 206  
      * Returns the Javadoc comment before the specified line.
 207  
      * A return value of <code>null</code> means there is no such comment.
 208  
      * @return the Javadoc comment, or <code>null</code> if none
 209  
      * @param aLineNo the line number to check before
 210  
      **/
 211  
     public TextBlock getJavadocBefore(int aLineNo)
 212  
     {
 213  
         // Lines start at 1 to the callers perspective, so need to take off 2
 214  962
         int lineNo = aLineNo - 2;
 215  
 
 216  
         // skip blank lines
 217  1154
         while ((lineNo > 0) && (lineIsBlank(lineNo) || lineIsComment(lineNo))) {
 218  192
             lineNo--;
 219  
         }
 220  
 
 221  962
         return mJavadocComments.get(lineNo);
 222  
     }
 223  
 
 224  
     /**
 225  
      * Get a single line.
 226  
      * For internal use only, as getText().get(lineNo) is just as
 227  
      * suitable for external use and avoids method duplication.
 228  
      * @param aLineNo the number of the line to get
 229  
      * @return the corresponding line, without terminator
 230  
      * @throws IndexOutOfBoundsException if lineNo is invalid
 231  
      */
 232  
     private String line(int aLineNo)
 233  
     {
 234  24318
         return mText.get(aLineNo);
 235  
     }
 236  
 
 237  
     /**
 238  
      * Get the full text of the file.
 239  
      * @return an object containing the full text of the file
 240  
      */
 241  
     public FileText getText()
 242  
     {
 243  749
         return mText;
 244  
     }
 245  
 
 246  
     /** @return the lines in the file */
 247  
     public String[] getLines()
 248  
     {
 249  21335
         return mText.toLinesArray();
 250  
     }
 251  
 
 252  
     /** @return the name of the file */
 253  
     public String getFilename()
 254  
     {
 255  1533
         return mFilename;
 256  
     }
 257  
 
 258  
     /**
 259  
      * Checks if the specified line is blank.
 260  
      * @param aLineNo the line number to check
 261  
      * @return if the specified line consists only of tabs and spaces.
 262  
      **/
 263  
     public boolean lineIsBlank(int aLineNo)
 264  
     {
 265  
         // possible improvement: avoid garbage creation in trim()
 266  1242
         return "".equals(line(aLineNo).trim());
 267  
     }
 268  
 
 269  
     /**
 270  
      * Checks if the specified line is a single-line comment without code.
 271  
      * @param aLineNo  the line number to check
 272  
      * @return if the specified line consists of only a single line comment
 273  
      *         without code.
 274  
      **/
 275  
     public boolean lineIsComment(int aLineNo)
 276  
     {
 277  1082
         return MATCH_SINGLELINE_COMMENT.matcher(line(aLineNo)).matches();
 278  
     }
 279  
 
 280  
     /**
 281  
      * Checks if the specified position intersects with a comment.
 282  
      * @param aStartLineNo the starting line number
 283  
      * @param aStartColNo the starting column number
 284  
      * @param aEndLineNo the ending line number
 285  
      * @param aEndColNo the ending column number
 286  
      * @return true if the positions intersects with a comment.
 287  
      **/
 288  
     public boolean hasIntersectionWithComment(int aStartLineNo,
 289  
             int aStartColNo, int aEndLineNo, int aEndColNo)
 290  
     {
 291  
         // Check C comments (all comments should be checked)
 292  70
         final Collection<List<TextBlock>> values = mCComments.values();
 293  70
         for (final List<TextBlock> row : values) {
 294  1096
             for (final TextBlock comment : row) {
 295  1120
                 if (comment.intersects(aStartLineNo, aStartColNo, aEndLineNo,
 296  
                         aEndColNo))
 297  
                 {
 298  42
                     return true;
 299  
                 }
 300  
             }
 301  
         }
 302  
 
 303  
         // Check CPP comments (line searching is possible)
 304  28
         for (int lineNumber = aStartLineNo; lineNumber <= aEndLineNo;
 305  4
              lineNumber++)
 306  
         {
 307  28
             final TextBlock comment = mCPlusPlusComments.get(lineNumber);
 308  28
             if ((comment != null)
 309  
                     && comment.intersects(aStartLineNo, aStartColNo,
 310  
                             aEndLineNo, aEndColNo))
 311  
             {
 312  24
                 return true;
 313  
             }
 314  
         }
 315  4
         return false;
 316  
     }
 317  
 
 318  
     /**
 319  
      * Checks if the current file is a package-info.java file.
 320  
      * @return true if the package file.
 321  
      */
 322  
     public boolean inPackageInfo()
 323  
     {
 324  32
         return this.getFilename().endsWith("package-info.java");
 325  
     }
 326  
 }