Coverage Report - com.puppycrawl.tools.checkstyle.checks.imports.ImportControlCheck
 
Classes in this File Line Coverage Branch Coverage Complexity
ImportControlCheck
100%
40/40
100%
16/16
2
 
 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.checks.imports;
 21  
 
 22  
 import java.net.URI;
 23  
 import java.util.Collections;
 24  
 import java.util.Set;
 25  
 import java.util.regex.Pattern;
 26  
 
 27  
 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
 28  
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 29  
 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
 30  
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 31  
 import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
 32  
 import com.puppycrawl.tools.checkstyle.api.FullIdent;
 33  
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
 34  
 
 35  
 /**
 36  
  * Check that controls what packages can be imported in each package. Useful
 37  
  * for ensuring that application layering is not violated. Ideas on how the
 38  
  * check can be improved include support for:
 39  
  * <ul>
 40  
  * <li>
 41  
  * Change the default policy that if a package being checked does not
 42  
  * match any guards, then it is allowed. Currently defaults to disallowed.
 43  
  * </li>
 44  
  * </ul>
 45  
  *
 46  
  * @author Oliver Burn
 47  
  */
 48  
 @FileStatefulCheck
 49  39
 public class ImportControlCheck extends AbstractCheck implements ExternalResourceHolder {
 50  
 
 51  
     /**
 52  
      * A key is pointing to the warning message text in "messages.properties"
 53  
      * file.
 54  
      */
 55  
     public static final String MSG_MISSING_FILE = "import.control.missing.file";
 56  
 
 57  
     /**
 58  
      * A key is pointing to the warning message text in "messages.properties"
 59  
      * file.
 60  
      */
 61  
     public static final String MSG_UNKNOWN_PKG = "import.control.unknown.pkg";
 62  
 
 63  
     /**
 64  
      * A key is pointing to the warning message text in "messages.properties"
 65  
      * file.
 66  
      */
 67  
     public static final String MSG_DISALLOWED = "import.control.disallowed";
 68  
 
 69  
     /**
 70  
      * A part of message for exception.
 71  
      */
 72  
     private static final String UNABLE_TO_LOAD = "Unable to load ";
 73  
 
 74  
     /** Location of import control file. */
 75  
     private String fileLocation;
 76  
 
 77  
     /** The filepath pattern this check applies to. */
 78  39
     private Pattern path = Pattern.compile(".*");
 79  
     /** Whether to process the current file. */
 80  
     private boolean processCurrentFile;
 81  
 
 82  
     /** The root package controller. */
 83  
     private ImportControl root;
 84  
     /** The package doing the import. */
 85  
     private String packageName;
 86  
 
 87  
     /**
 88  
      * The package controller for the current file. Used for performance
 89  
      * optimisation.
 90  
      */
 91  
     private ImportControl currentImportControl;
 92  
 
 93  
     @Override
 94  
     public int[] getDefaultTokens() {
 95  59
         return getRequiredTokens();
 96  
     }
 97  
 
 98  
     @Override
 99  
     public int[] getAcceptableTokens() {
 100  5
         return getRequiredTokens();
 101  
     }
 102  
 
 103  
     @Override
 104  
     public int[] getRequiredTokens() {
 105  123
         return new int[] {TokenTypes.PACKAGE_DEF, TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT, };
 106  
     }
 107  
 
 108  
     @Override
 109  
     public void beginTree(DetailAST rootAST) {
 110  24
         currentImportControl = null;
 111  24
         processCurrentFile = path.matcher(getFileContents().getFileName()).find();
 112  24
     }
 113  
 
 114  
     @Override
 115  
     public void visitToken(DetailAST ast) {
 116  112
         if (processCurrentFile) {
 117  102
             if (ast.getType() == TokenTypes.PACKAGE_DEF) {
 118  21
                 if (root == null) {
 119  3
                     log(ast, MSG_MISSING_FILE);
 120  
                 }
 121  
                 else {
 122  18
                     packageName = getPackageText(ast);
 123  18
                     currentImportControl = root.locateFinest(packageName);
 124  18
                     if (currentImportControl == null) {
 125  1
                         log(ast, MSG_UNKNOWN_PKG);
 126  
                     }
 127  
                 }
 128  
             }
 129  81
             else if (currentImportControl != null) {
 130  65
                 final String importText = getImportText(ast);
 131  65
                 final AccessResult access =
 132  65
                         currentImportControl.checkAccess(packageName, importText);
 133  65
                 if (access != AccessResult.ALLOWED) {
 134  27
                     log(ast, MSG_DISALLOWED, importText);
 135  
                 }
 136  
             }
 137  
         }
 138  112
     }
 139  
 
 140  
     @Override
 141  
     public Set<String> getExternalResourceLocations() {
 142  2
         return Collections.singleton(fileLocation);
 143  
     }
 144  
 
 145  
     /**
 146  
      * Returns package text.
 147  
      * @param ast PACKAGE_DEF ast node
 148  
      * @return String that represents full package name
 149  
      */
 150  
     private static String getPackageText(DetailAST ast) {
 151  18
         final DetailAST nameAST = ast.getLastChild().getPreviousSibling();
 152  18
         return FullIdent.createFullIdent(nameAST).getText();
 153  
     }
 154  
 
 155  
     /**
 156  
      * Returns import text.
 157  
      * @param ast ast node that represents import
 158  
      * @return String that represents importing class
 159  
      */
 160  
     private static String getImportText(DetailAST ast) {
 161  
         final FullIdent imp;
 162  65
         if (ast.getType() == TokenTypes.IMPORT) {
 163  50
             imp = FullIdent.createFullIdentBelow(ast);
 164  
         }
 165  
         else {
 166  
             // know it is a static import
 167  30
             imp = FullIdent.createFullIdent(ast
 168  15
                     .getFirstChild().getNextSibling());
 169  
         }
 170  65
         return imp.getText();
 171  
     }
 172  
 
 173  
     /**
 174  
      * Set the name for the file containing the import control
 175  
      * configuration. It can also be a URL or resource in the classpath.
 176  
      * It will cause the file to be loaded.
 177  
      * @param uri the uri of the file to load.
 178  
      * @throws IllegalArgumentException on error loading the file.
 179  
      */
 180  
     public void setFile(URI uri) {
 181  
         // Handle empty param
 182  28
         if (uri != null) {
 183  
             try {
 184  26
                 root = ImportControlLoader.load(uri);
 185  24
                 fileLocation = uri.toString();
 186  
             }
 187  2
             catch (CheckstyleException ex) {
 188  2
                 throw new IllegalArgumentException(UNABLE_TO_LOAD + uri, ex);
 189  24
             }
 190  
         }
 191  26
     }
 192  
 
 193  
     /**
 194  
      * Set the file path pattern that this check applies to.
 195  
      * @param pattern the file path regex this check should apply to.
 196  
      */
 197  
     public void setPath(Pattern pattern) {
 198  5
         path = pattern;
 199  5
     }
 200  
 }