Coverage Report - com.puppycrawl.tools.checkstyle.checks.whitespace.GenericWhitespaceCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
GenericWhitespaceCheck
98%
51/52
85%
58/68
6.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.checks.whitespace;
 20  
 
 21  
 import com.puppycrawl.tools.checkstyle.api.Check;
 22  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 23  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 24  
 import com.puppycrawl.tools.checkstyle.api.Utils;
 25  
 
 26  
 /**
 27  
  * Checks that the whitespace around the Generic tokens < and > are
 28  
  * correct to the <i>typical</i> convention. The convention is not configurable.
 29  
  * @author Oliver Burn
 30  
  */
 31  1
 public class GenericWhitespaceCheck extends Check
 32  
 {
 33  
     /** Used to count the depth of a Generic expression. */
 34  
     private int mDepth;
 35  
 
 36  
     @Override
 37  
     public int[] getDefaultTokens()
 38  
     {
 39  1
         return new int[] {TokenTypes.GENERIC_START, TokenTypes.GENERIC_END};
 40  
     }
 41  
 
 42  
     @Override
 43  
     public void beginTree(DetailAST aRootAST)
 44  
     {
 45  
         // Reset for each tree, just incase there are errors in preceeding
 46  
         // trees.
 47  1
         mDepth = 0;
 48  1
     }
 49  
 
 50  
     @Override
 51  
     public void visitToken(DetailAST aAST)
 52  
     {
 53  76
         if (aAST.getType() == TokenTypes.GENERIC_START) {
 54  38
             processStart(aAST);
 55  38
             mDepth++;
 56  
         }
 57  38
         else if (aAST.getType() == TokenTypes.GENERIC_END) {
 58  38
             processEnd(aAST);
 59  38
             mDepth--;
 60  
         }
 61  76
     }
 62  
 
 63  
     /**
 64  
      * Checks the token for the end of Generics.
 65  
      * @param aAST the token to check
 66  
      */
 67  
     private void processEnd(DetailAST aAST)
 68  
     {
 69  38
         final String line = getLines()[aAST.getLineNo() - 1];
 70  38
         final int before = aAST.getColumnNo() - 1;
 71  38
         final int after = aAST.getColumnNo() + 1;
 72  
 
 73  38
         if ((0 <= before) && Character.isWhitespace(line.charAt(before))
 74  
                 && !Utils.whitespaceBefore(before, line))
 75  
         {
 76  6
             log(aAST.getLineNo(), before, "ws.preceded", ">");
 77  
         }
 78  
 
 79  38
         if (after < line.length()) {
 80  
 
 81  
             // Check if the last Generic, in which case must be a whitespace
 82  
             // or a '(),[.'.
 83  38
             if (1 == mDepth) {
 84  28
                 final char charAfter = line.charAt(after);
 85  
 
 86  
                 // Need to handle a number of cases. First is:
 87  
                 //    Collections.<Object>emptySet();
 88  
                 //                        ^
 89  
                 //                        +--- whitespace not allowed
 90  28
                 if ((aAST.getParent().getType() == TokenTypes.TYPE_ARGUMENTS)
 91  
                     && (aAST.getParent().getParent().getType()
 92  
                         == TokenTypes.DOT)
 93  
                     && (aAST.getParent().getParent().getParent().getType()
 94  
                         == TokenTypes.METHOD_CALL))
 95  
                 {
 96  2
                     if (Character.isWhitespace(charAfter)) {
 97  1
                         log(aAST.getLineNo(), after, "ws.followed", ">");
 98  
                     }
 99  
                 }
 100  26
                 else if (!Character.isWhitespace(charAfter)
 101  
                     && ('(' != charAfter) && (')' != charAfter)
 102  
                     && (',' != charAfter) && ('[' != charAfter)
 103  
                     && ('.' != charAfter))
 104  
                 {
 105  1
                     log(aAST.getLineNo(), after, "ws.illegalFollow", ">");
 106  
                 }
 107  28
             }
 108  
             else {
 109  
                 // In a nested Generic type, so can only be a '>' or ',' or '&'
 110  
 
 111  
                 // In case of several extends definitions:
 112  
                 //
 113  
                 //   class IntEnumValueType<E extends Enum<E> & IntEnum>
 114  
                 //                                          ^
 115  
                 //   should be whitespace if followed by & -+
 116  
                 //
 117  10
                 final int indexOfAmp = line.indexOf('&', after);
 118  10
                 if ((indexOfAmp != -1)
 119  
                     && whitespaceBetween(after, indexOfAmp, line))
 120  
                 {
 121  3
                     if (indexOfAmp - after == 0) {
 122  1
                         log(aAST.getLineNo(), after, "ws.notPreceded", "&");
 123  
                     }
 124  2
                     else if (indexOfAmp - after != 1) {
 125  1
                         log(aAST.getLineNo(), after, "ws.followed", ">");
 126  
                     }
 127  
                 }
 128  7
                 else if ((line.charAt(after) != '>')
 129  
                          && (line.charAt(after) != ','))
 130  
                 {
 131  2
                     log(aAST.getLineNo(), after, "ws.followed", ">");
 132  
                 }
 133  
             }
 134  
         }
 135  38
     }
 136  
 
 137  
     /**
 138  
      * Checks the token for the start of Generics.
 139  
      * @param aAST the token to check
 140  
      */
 141  
     private void processStart(DetailAST aAST)
 142  
     {
 143  38
         final String line = getLines()[aAST.getLineNo() - 1];
 144  38
         final int before = aAST.getColumnNo() - 1;
 145  38
         final int after = aAST.getColumnNo() + 1;
 146  
 
 147  
         // Need to handle two cases as in:
 148  
         //
 149  
         //   public static <T> Callable<T> callable(Runnable task, T result)
 150  
         //                 ^           ^
 151  
         //      ws reqd ---+           +--- whitespace NOT required
 152  
         //
 153  38
         if (0 <= before) {
 154  
             // Detect if the first case
 155  38
             final DetailAST parent = aAST.getParent();
 156  38
             final DetailAST grandparent = parent.getParent();
 157  38
             if ((TokenTypes.TYPE_PARAMETERS == parent.getType())
 158  
                 && ((TokenTypes.CTOR_DEF == grandparent.getType())
 159  
                     || (TokenTypes.METHOD_DEF == grandparent.getType())))
 160  
             {
 161  
                 // Require whitespace
 162  4
                 if (!Character.isWhitespace(line.charAt(before))) {
 163  1
                     log(aAST.getLineNo(), before, "ws.notPreceded", "<");
 164  
                 }
 165  
             }
 166  
             // Whitespace not required
 167  34
             else if (Character.isWhitespace(line.charAt(before))
 168  
                 && !Utils.whitespaceBefore(before, line))
 169  
             {
 170  7
                 log(aAST.getLineNo(), before, "ws.preceded", "<");
 171  
             }
 172  
         }
 173  
 
 174  38
         if ((after < line.length())
 175  
                 && Character.isWhitespace(line.charAt(after)))
 176  
         {
 177  6
             log(aAST.getLineNo(), after, "ws.followed", "<");
 178  
         }
 179  38
     }
 180  
 
 181  
     /**
 182  
      * Returns whether the specified string contains only whitespace between
 183  
      * specified indices.
 184  
      *
 185  
      * @param aFromIndex the index to start the search from. Inclusive
 186  
      * @param aToIndex the index to finish the search. Exclusive
 187  
      * @param aLine the line to check
 188  
      * @return whether there are only whitespaces (or nothing)
 189  
      */
 190  
     private static boolean whitespaceBetween(
 191  
         int aFromIndex, int aToIndex, String aLine)
 192  
     {
 193  6
         for (int i = aFromIndex; i < aToIndex; i++) {
 194  3
             if (!Character.isWhitespace(aLine.charAt(i))) {
 195  0
                 return false;
 196  
             }
 197  
         }
 198  3
         return true;
 199  
     }
 200  
 }