Coverage Report - com.puppycrawl.tools.checkstyle.checks.coding.IllegalTypeCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
IllegalTypeCheck
82%
47/57
76%
23/30
2
 
 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.coding;
 20  
 
 21  
 import com.google.common.collect.Sets;
 22  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 23  
 import com.puppycrawl.tools.checkstyle.api.FullIdent;
 24  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 25  
 import com.puppycrawl.tools.checkstyle.checks.AbstractFormatCheck;
 26  
 import com.puppycrawl.tools.checkstyle.checks.CheckUtils;
 27  
 import java.util.Set;
 28  
 
 29  
 /**
 30  
  * <p>
 31  
  * Checks that particular class are never used as types in variable
 32  
  * declarations, return values or parameters. Includes
 33  
  * a pattern check that by default disallows abstract classes.
 34  
  * </p>
 35  
  * <p>
 36  
  * Rationale:
 37  
  * Helps reduce coupling on concrete classes. In addition abstract
 38  
  * classes should be thought of a convenience base class
 39  
  * implementations of interfaces and as such are not types themsleves.
 40  
  * </p>
 41  
  * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
 42  
  */
 43  
 public final class IllegalTypeCheck extends AbstractFormatCheck
 44  
 {
 45  
     /** Default value of pattern for illegal class name. */
 46  
     private static final String DEFAULT_FORMAT = "^(.*[\\.])?Abstract.*$";
 47  
     /** Abstract classes legal by default. */
 48  1
     private static final String[] DEFAULT_LEGAL_ABSTRACT_NAMES = {};
 49  
     /** Types illegal by default. */
 50  1
     private static final String[] DEFAULT_ILLEGAL_TYPES = {
 51  
         "GregorianCalendar",
 52  
         "Hashtable",
 53  
         "HashSet",
 54  
         "HashMap",
 55  
         "ArrayList",
 56  
         "LinkedList",
 57  
         "LinkedHashMap",
 58  
         "LinkedHashSet",
 59  
         "TreeSet",
 60  
         "TreeMap",
 61  
         "Vector",
 62  
         "java.util.GregorianCalendar",
 63  
         "java.util.Hashtable",
 64  
         "java.util.HashSet",
 65  
         "java.util.HashMap",
 66  
         "java.util.ArrayList",
 67  
         "java.util.LinkedList",
 68  
         "java.util.LinkedHashMap",
 69  
         "java.util.LinkedHashSet",
 70  
         "java.util.TreeSet",
 71  
         "java.util.TreeMap",
 72  
         "java.util.Vector",
 73  
     };
 74  
 
 75  
     /** Default ignored method names. */
 76  1
     private static final String[] DEFAULT_IGNORED_METHOD_NAMES = {
 77  
         "getInitialContext",
 78  
         "getEnvironment",
 79  
     };
 80  
 
 81  
     /** illegal classes. */
 82  4
     private final Set<String> mIllegalClassNames = Sets.newHashSet();
 83  
     /** legal abstract classes. */
 84  4
     private final Set<String> mLegalAbstractClassNames = Sets.newHashSet();
 85  
     /** methods which should be ignored. */
 86  4
     private final Set<String> mIgnoredMethodNames = Sets.newHashSet();
 87  
 
 88  
     /** Creates new instance of the check. */
 89  
     public IllegalTypeCheck()
 90  
     {
 91  4
         super(DEFAULT_FORMAT);
 92  4
         setIllegalClassNames(DEFAULT_ILLEGAL_TYPES);
 93  4
         setLegalAbstractClassNames(DEFAULT_LEGAL_ABSTRACT_NAMES);
 94  4
         setIgnoredMethodNames(DEFAULT_IGNORED_METHOD_NAMES);
 95  4
     }
 96  
 
 97  
     @Override
 98  
     public int[] getDefaultTokens()
 99  
     {
 100  4
         return new int[] {
 101  
             TokenTypes.VARIABLE_DEF,
 102  
             TokenTypes.PARAMETER_DEF,
 103  
             TokenTypes.METHOD_DEF,
 104  
         };
 105  
     }
 106  
 
 107  
     @Override
 108  
     public void visitToken(DetailAST aAST)
 109  
     {
 110  24
         switch (aAST.getType()) {
 111  
         case TokenTypes.METHOD_DEF:
 112  8
             visitMethodDef(aAST);
 113  8
             break;
 114  
         case TokenTypes.VARIABLE_DEF:
 115  16
             visitVariableDef(aAST);
 116  16
             break;
 117  
         case TokenTypes.PARAMETER_DEF:
 118  0
             visitParameterDef(aAST);
 119  0
             break;
 120  
         default:
 121  0
             throw new IllegalStateException(aAST.toString());
 122  
         }
 123  24
     }
 124  
 
 125  
     /**
 126  
      * Checks return type of a given method.
 127  
      * @param aAST method for check.
 128  
      */
 129  
     private void visitMethodDef(DetailAST aAST)
 130  
     {
 131  8
         if (isCheckedMethod(aAST)) {
 132  7
             checkClassName(aAST);
 133  
         }
 134  8
     }
 135  
 
 136  
     /**
 137  
      * Checks type of parameters.
 138  
      * @param aAST parameter list for check.
 139  
      */
 140  
     private void visitParameterDef(DetailAST aAST)
 141  
     {
 142  0
         final DetailAST grandParentAST = aAST.getParent().getParent();
 143  
 
 144  0
         if ((grandParentAST.getType() == TokenTypes.METHOD_DEF)
 145  
             && isCheckedMethod(grandParentAST))
 146  
         {
 147  0
             checkClassName(aAST);
 148  
         }
 149  0
     }
 150  
 
 151  
     /**
 152  
      * Checks type of given variable.
 153  
      * @param aAST variable to check.
 154  
      */
 155  
     private void visitVariableDef(DetailAST aAST)
 156  
     {
 157  16
         checkClassName(aAST);
 158  16
     }
 159  
 
 160  
     /**
 161  
      * Checks type of given method, parameter or variable.
 162  
      * @param aAST node to check.
 163  
      */
 164  
     private void checkClassName(DetailAST aAST)
 165  
     {
 166  23
         final DetailAST type = aAST.findFirstToken(TokenTypes.TYPE);
 167  23
         final FullIdent ident = CheckUtils.createFullType(type);
 168  
 
 169  23
         if (isMatchingClassName(ident.getText())) {
 170  12
             log(ident.getLineNo(), ident.getColumnNo(),
 171  
                 "illegal.type", ident.getText());
 172  
         }
 173  23
     }
 174  
 
 175  
     /**
 176  
      * @param aClassName class name to check.
 177  
      * @return true if given class name is one of illegal classes
 178  
      *         or if it matches to abstract class names pattern.
 179  
      */
 180  
     private boolean isMatchingClassName(String aClassName)
 181  
     {
 182  23
         return mIllegalClassNames.contains(aClassName)
 183  
             || (!mLegalAbstractClassNames.contains(aClassName)
 184  
                 && getRegexp().matcher(aClassName).find());
 185  
     }
 186  
 
 187  
     /**
 188  
      * @param aAST method def to check.
 189  
      * @return true if we should check this method.
 190  
      */
 191  
     private boolean isCheckedMethod(DetailAST aAST)
 192  
     {
 193  8
         final String methodName =
 194  
             aAST.findFirstToken(TokenTypes.IDENT).getText();
 195  8
         return !mIgnoredMethodNames.contains(methodName);
 196  
     }
 197  
 
 198  
     /**
 199  
      * Set the list of illegal variable types.
 200  
      * @param aClassNames array of illegal variable types
 201  
      */
 202  
     public void setIllegalClassNames(String[] aClassNames)
 203  
     {
 204  4
         mIllegalClassNames.clear();
 205  92
         for (String name : aClassNames) {
 206  88
             mIllegalClassNames.add(name);
 207  88
             final int lastDot = name.lastIndexOf(".");
 208  88
             if ((lastDot > 0) && (lastDot < (name.length() - 1))) {
 209  44
                 final String shortName =
 210  
                     name.substring(name.lastIndexOf(".") + 1);
 211  44
                 mIllegalClassNames.add(shortName);
 212  
             }
 213  
         }
 214  4
     }
 215  
 
 216  
     /**
 217  
      * Get the list of illegal variable types.
 218  
      * @return array of illegal variable types
 219  
      */
 220  
     public String[] getIllegalClassNames()
 221  
     {
 222  0
         return mIllegalClassNames.toArray(
 223  
             new String[mIllegalClassNames.size()]);
 224  
     }
 225  
 
 226  
     /**
 227  
      * Set the list of ignore method names.
 228  
      * @param aMethodNames array of ignored method names
 229  
      */
 230  
     public void setIgnoredMethodNames(String[] aMethodNames)
 231  
     {
 232  5
         mIgnoredMethodNames.clear();
 233  14
         for (String element : aMethodNames) {
 234  9
             mIgnoredMethodNames.add(element);
 235  
         }
 236  5
     }
 237  
 
 238  
     /**
 239  
      * Get the list of ignored method names.
 240  
      * @return array of ignored method names
 241  
      */
 242  
     public String[] getIgnoredMethodNames()
 243  
     {
 244  0
         return mIgnoredMethodNames.toArray(
 245  
             new String[mIgnoredMethodNames.size()]);
 246  
     }
 247  
 
 248  
     /**
 249  
      * Set the list of legal abstract class names.
 250  
      * @param aClassNames array of legal abstract class names
 251  
      */
 252  
     public void setLegalAbstractClassNames(String[] aClassNames)
 253  
     {
 254  5
         mLegalAbstractClassNames.clear();
 255  6
         for (String element : aClassNames) {
 256  1
             mLegalAbstractClassNames.add(element);
 257  
         }
 258  5
     }
 259  
 
 260  
     /**
 261  
      * Get the list of legal abstract class names.
 262  
      * @return array of legal abstract class names
 263  
      */
 264  
     public String[] getLegalAbstractClassNames()
 265  
     {
 266  0
         return mLegalAbstractClassNames.toArray(
 267  
             new String[mLegalAbstractClassNames.size()]);
 268  
     }
 269  
 }