001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2016 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.imports; 021 022import java.io.File; 023import java.net.URI; 024import java.util.Set; 025 026import org.apache.commons.beanutils.ConversionException; 027 028import com.google.common.collect.ImmutableSet; 029import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 030import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 031import com.puppycrawl.tools.checkstyle.api.DetailAST; 032import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder; 033import com.puppycrawl.tools.checkstyle.api.FullIdent; 034import com.puppycrawl.tools.checkstyle.api.TokenTypes; 035import com.puppycrawl.tools.checkstyle.utils.CommonUtils; 036 037/** 038 * Check that controls what packages can be imported in each package. Useful 039 * for ensuring that application layering is not violated. Ideas on how the 040 * check can be improved include support for: 041 * <ul> 042 * <li> 043 * Change the default policy that if a package being checked does not 044 * match any guards, then it is allowed. Currently defaults to disallowed. 045 * </li> 046 * </ul> 047 * 048 * @author Oliver Burn 049 */ 050public class ImportControlCheck extends AbstractCheck implements ExternalResourceHolder { 051 052 /** 053 * A key is pointing to the warning message text in "messages.properties" 054 * file. 055 */ 056 public static final String MSG_MISSING_FILE = "import.control.missing.file"; 057 058 /** 059 * A key is pointing to the warning message text in "messages.properties" 060 * file. 061 */ 062 public static final String MSG_UNKNOWN_PKG = "import.control.unknown.pkg"; 063 064 /** 065 * A key is pointing to the warning message text in "messages.properties" 066 * file. 067 */ 068 public static final String MSG_DISALLOWED = "import.control.disallowed"; 069 070 /** 071 * A part of message for exception. 072 */ 073 private static final String UNABLE_TO_LOAD = "Unable to load "; 074 075 /** Location of import control file. */ 076 private String fileLocation; 077 078 /** The root package controller. */ 079 private PkgControl root; 080 /** The package doing the import. */ 081 private String inPkg; 082 083 /** 084 * The package controller for the current file. Used for performance 085 * optimisation. 086 */ 087 private PkgControl currentLeaf; 088 089 @Override 090 public int[] getDefaultTokens() { 091 return getAcceptableTokens(); 092 } 093 094 @Override 095 public int[] getAcceptableTokens() { 096 return new int[] {TokenTypes.PACKAGE_DEF, TokenTypes.IMPORT, 097 TokenTypes.STATIC_IMPORT, }; 098 } 099 100 @Override 101 public int[] getRequiredTokens() { 102 return getAcceptableTokens(); 103 } 104 105 @Override 106 public void beginTree(final DetailAST rootAST) { 107 currentLeaf = null; 108 } 109 110 @Override 111 public void visitToken(final DetailAST ast) { 112 if (ast.getType() == TokenTypes.PACKAGE_DEF) { 113 final DetailAST nameAST = ast.getLastChild().getPreviousSibling(); 114 final FullIdent full = FullIdent.createFullIdent(nameAST); 115 if (root == null) { 116 log(nameAST, MSG_MISSING_FILE); 117 } 118 else { 119 inPkg = full.getText(); 120 currentLeaf = root.locateFinest(inPkg); 121 if (currentLeaf == null) { 122 log(nameAST, MSG_UNKNOWN_PKG); 123 } 124 } 125 } 126 else if (currentLeaf != null) { 127 final FullIdent imp; 128 if (ast.getType() == TokenTypes.IMPORT) { 129 imp = FullIdent.createFullIdentBelow(ast); 130 } 131 else { 132 // know it is a static import 133 imp = FullIdent.createFullIdent(ast 134 .getFirstChild().getNextSibling()); 135 } 136 final AccessResult access = currentLeaf.checkAccess(imp.getText(), 137 inPkg); 138 if (access != AccessResult.ALLOWED) { 139 log(ast, MSG_DISALLOWED, imp.getText()); 140 } 141 } 142 } 143 144 @Override 145 public Set<String> getExternalResourceLocations() { 146 return ImmutableSet.of(fileLocation); 147 } 148 149 /** 150 * Set the name for the file containing the import control 151 * configuration. It will cause the file to be loaded. 152 * @param name the name of the file to load. 153 * @throws ConversionException on error loading the file. 154 */ 155 public void setFile(final String name) { 156 // Handle empty param 157 if (!CommonUtils.isBlank(name)) { 158 try { 159 root = ImportControlLoader.load(new File(name).toURI()); 160 fileLocation = name; 161 } 162 catch (final CheckstyleException ex) { 163 throw new ConversionException(UNABLE_TO_LOAD + name, ex); 164 } 165 } 166 } 167 168 /** 169 * Set the parameter for the url containing the import control 170 * configuration. It will cause the url to be loaded. 171 * @param url the url of the file to load. 172 * @throws ConversionException on error loading the file. 173 */ 174 public void setUrl(final String url) { 175 // Handle empty param 176 if (!CommonUtils.isBlank(url)) { 177 final URI uri; 178 try { 179 uri = URI.create(url); 180 } 181 catch (final IllegalArgumentException ex) { 182 throw new ConversionException("Syntax error in url " + url, ex); 183 } 184 try { 185 root = ImportControlLoader.load(uri); 186 fileLocation = url; 187 } 188 catch (final CheckstyleException ex) { 189 throw new ConversionException(UNABLE_TO_LOAD + url, ex); 190 } 191 } 192 } 193}