Class ImportControlCheck
- java.lang.Object
-
- com.puppycrawl.tools.checkstyle.api.AutomaticBean
-
- com.puppycrawl.tools.checkstyle.api.AbstractViolationReporter
-
- com.puppycrawl.tools.checkstyle.api.AbstractCheck
-
- com.puppycrawl.tools.checkstyle.checks.imports.ImportControlCheck
-
- All Implemented Interfaces:
Configurable
,Contextualizable
,ExternalResourceHolder
public class ImportControlCheck extends AbstractCheck implements ExternalResourceHolder
Controls what can be imported in each package and file. Useful for ensuring that application layering rules are not violated, especially on large projects.
You can control imports based on the a package name or based on the file name. When controlling packages, all files and sub-packages in the declared package will be controlled by this check. To specify differences between a main package and a sub-package, you must define the sub-package inside the main package. When controlling file, only the file name is considered and only files processed by TreeWalker. The file's extension is ignored.
Short description of the behaviour:
-
Check starts checking from the longest matching subpackage (later 'current subpackage') or
the first file name match described inside import control file to package defined in class file.
- The longest matching subpackage is found by starting with the root package and examining if the any of the sub-packages or file definitions match the current class' package or file name.
- If a file name is matched first, that is considered the longest match and becomes the current file/subpackage.
- If another subpackage is matched, then it's subpackages and file names are examined for the next longest match and the process repeats recursively.
- If no subpackages or file names are matched, the current subpackage is then used.
- Order of rules in the same subpackage/root are defined by the order of declaration in the XML file, which is from top (first) to bottom (last).
- If there is matching allow/disallow rule inside the current file/subpackage then the Check returns the first "allowed" or "disallowed" message.
- If there is no matching allow/disallow rule inside the current file/subpackage then it continues checking in the parent subpackage.
- If there is no matching allow/disallow rule in any of the files/subpackages, including the root level (import-control), then the import is disallowed by default.
The DTD for a import control XML document is at https://checkstyle.org/dtds/import_control_1_4.dtd. It contains documentation on each of the elements and attributes.
The check validates a XML document when it loads the document. To validate against the above DTD, include the following document type declaration in your XML document:
<!DOCTYPE import-control PUBLIC "-//Checkstyle//DTD ImportControl Configuration 1.4//EN" "https://checkstyle.org/dtds/import_control_1_4.dtd">
-
Property
file
- Specify the location of the file containing the import control configuration. It can be a regular file, URL or resource path. It will try loading the path as a URL first, then as a file, and finally as a resource. Type isjava.net.URI
. Default value isnull
. -
Property
path
- Specify the regular expression of file paths to which this check should apply. Files that don't match the pattern will not be checked. The pattern will be matched against the full absolute file path. Type isjava.util.regex.Pattern
. Default value is".*"
.
To configure the check using an import control file called "config/import-control.xml", then have the following:
<module name="ImportControl"> <property name="file" value="config/import-control.xml"/> </module>
To configure the check to only check the "src/main" directory using an import control file called "config/import-control.xml", then have the following:
<module name="ImportControl"> <property name="file" value="config/import-control.xml"/> <property name="path" value="^.*[\\/]src[\\/]main[\\/].*$"/> </module>
In the example below access to package
com.puppycrawl.tools.checkstyle.checks
and its subpackages is allowed from anywhere incom.puppycrawl.tools.checkstyle
except from thefilters
subpackage where access to allcheck
's subpackages is disallowed. Twojava.lang.ref
classes are allowed by virtue of one regular expression instead of listing them in two separate allow rules (as it is done with theFiles
andClassPath
classes).<import-control pkg="com.puppycrawl.tools.checkstyle"> <disallow pkg="sun"/> <allow pkg="com.puppycrawl.tools.checkstyle.api"/> <allow pkg="com.puppycrawl.tools.checkstyle.checks"/> <allow class="com.google.common.io.Files"/> <allow class="com.google.common.reflect.ClassPath"/> <subpackage name="filters"> <allow class="java\.lang\.ref\.(Weak|Soft)Reference" regex="true"/> <disallow pkg="com\.puppycrawl\.tools\.checkstyle\.checks\.[^.]+" regex="true"/> <disallow pkg="com.puppycrawl.tools.checkstyle.ant"/> <disallow pkg="com.puppycrawl.tools.checkstyle.gui"/> </subpackage> <subpackage name="dao"> <disallow pkg="javax.swing" exact-match="true"/> </subpackage> </import-control>
In the next example regular expressions are used to enforce a layering rule: In all
dao
packages it is not allowed to access UI layer code (ui
,awt
, andswing
). On the other hand it is not allowed to directly accessdao
andservice
layer fromui
packages. The root package is also a regular expression that is used to handle old and new domain name with the same rules.<import-control pkg="(de.olddomain|de.newdomain)\..*" regex="true"> <subpackage pkg="[^.]+\.dao" regex="true"> <disallow pkg=".*\.ui" regex="true"/> <disallow pkg=".*\.(awt|swing).\.*" regex="true"/> </subpackage> <subpackage pkg="[^.]+\.ui" regex="true"> <disallow pkg=".*\.(dao|service)" regex="true"/> </subpackage> </import-control>
In the next examples usage of
strategyOnMismatch
property is shown. This property defines strategy in a case when no matching allow/disallow rule was found. PropertystrategyOnMismatch
is attribute ofimport-control
andsubpackage
tags. Property can have following values forimport-control
tag:- disallowed (default value) - if there is no matching allow/disallow rule in any of the subpackages, including the root level (import-control), then the import is disallowed.
- allowed - if there is no matching allow/disallow rule in any of the subpackages, including the root level, then the import is allowed.
And following values for
subpackage
tags:- delegateToParent (default value) - if there is no matching allow/disallow rule inside the current subpackage, then it continues checking in the parent subpackage.
- allowed - if there is no matching allow/disallow rule inside the current subpackage, then the import is allowed.
- disallowed - if there is no matching allow/disallow rule inside the current subpackage, then the import is disallowed.
The following example demonstrates usage of
strategyOnMismatch
property forimport-control
tag. Here all imports are allowed exceptjava.awt.Image
andjava.io.File
classes.<import-control pkg="com.puppycrawl.tools.checkstyle.checks" strategyOnMismatch="allowed"> <disallow class="java.awt.Image"/> <disallow class="java.io.File"/> </import-control>
In the example below, any import is disallowed inside
com.puppycrawl.tools.checkstyle.checks.imports
package except imports from packagejavax.swing
and classjava.io.File
. However, any import is allowed in the classes outside ofcom.puppycrawl.tools.checkstyle.checks.imports
package.<import-control pkg="com.puppycrawl.tools.checkstyle.checks" strategyOnMismatch="allowed"> <subpackage name="imports" strategyOnMismatch="disallowed"> <allow pkg="javax.swing"/> <allow class="java.io.File"/> </subpackage> </import-control>
When
strategyOnMismatch
hasallowed
ordisallowed
value forsubpackage
tag, it makessubpackage
isolated from parent rules. In the next example, if no matching rule was found insidecom.puppycrawl.tools.checkstyle.checks.filters
then it continues checking in the parent subpackage, while forcom.puppycrawl.tools.checkstyle.checks.imports
import will be allowed by default.<import-control pkg="com.puppycrawl.tools.checkstyle.checks"> <allow class="java\.awt\.Image" regex="true"/> <allow class="java\..*\.File" local-only="true" regex="true"/> <subpackage name="imports" strategyOnMismatch="allowed"> <allow pkg="javax\.swing" regex="true"/> <allow pkg="java\.io" exact-match="true" local-only="true" regex="true"/> </subpackage> <subpackage name="filters"> <allow class="javax.util.Date"/> </subpackage> </import-control>
In the example below, only file names that end with "Panel", "View", or "Dialog" in the package
gui
are disallowed to have imports fromcom.mycompany.dao
and anyjdbc
package. In addition, only the file name named "PresentationModel" in the packagegui
are disallowed to have imports that matchjavax.swing.J*
. All other imports in the package are allowed.<import-control pkg="com.mycompany.billing"> <subpackage name="gui" strategyOnMismatch="allowed"> <file name=".*(Panel|View|Dialog)" regex="true"> <disallow pkg="com.mycompany.dao"/> <disallow pkg=".*\.jdbc" regex="true"/> </file> <file name="PresentationModel"> <disallow pkg="javax\.swing\.J.*" regex="true"/> </file> </subpackage> </import-control>
For a real-life import control file look at the file called import-control.xml which is part of the Checkstyle distribution.
Example of blacklist mode
To have a blacklist mode, it is required to have disallows inside subpackage and to have allow rule inside parent of the current subpackage to catch classes and packages those are not in the blacklist.
In the example below any import from
java.util
(java.util.List
,java.util.Date
) package is allowed exceptjava.util.Map
inside subpackagecom.puppycrawl.tools.checkstyle.filters
.<import-control pkg="com.puppycrawl.tools.checkstyle"> <allow pkg="java.util"/> <subpackage name="filters" > <disallow class="java.util.Map"/> </subpackage> </import-control>
In the next example imports
java.util.stream.Stream
andjava.util.stream.Collectors
are disallowed insidecom.puppycrawl.tools.checkstyle.checks.imports
package, but because of<allow pkg="java.util.stream"/>
every import fromjava.util.stream
is allowed except described ones.<import-control pkg="com.puppycrawl.tools.checkstyle.checks"> <allow pkg="java.util.stream"/> <subpackage name="imports"> <disallow class="java.util.stream.Stream"/> <disallow class="java.util.stream.Collectors"/> </subpackage> </import-control>
package com.puppycrawl.tools.checkstyle.checks.imports; import java.util.stream.Stream; // violation here import java.util.stream.Collectors; // violation here import java.util.stream.IntStream;
In the following example, all imports are allowed except the classes
java.util.Date
,java.util.List
and packagesun
.<import-control pkg="com.puppycrawl.tools.checkstyle.checks"> <allow pkg=".*" regex="true"/> <subpackage name="imports"> <disallow class="java.util.Date"/> <disallow class="java.util.List"/> <disallow pkg="sun"/> </subpackage> </import-control>
In the following example, all imports of the
java.util
package are allowed except thejava.util.Date
class.<import-control pkg="com.puppycrawl.tools.checkstyle.checks"> <disallow class="java.util.Date"/> <allow pkg="java.util"/> </import-control>
Notes on regular expressions
Regular expressions in import rules have to match either Java packages or classes. The language rules for packages and class names can be described by the following complicated regular expression that takes into account that Java names may contain any unicode letter, numbers, underscores, and dollar signs (see section 3.8 in the Java specs):
-
[\p{Letter}_$][\p{Letter}\p{Number}_$]*
or short[\p{L}_$][\p{L}\p{N}_$]*
for a class name or package component. -
([\p{L}_$][\p{L}\p{N}_$]*\.)*[\p{L}_$][\p{L}\p{N}_$]*
for a fully qualified name.
But it is not necessary to use these complicated expressions since no validation is required. Differentiating between package separator '.' and others is sufficient. Unfortunately '.' has a special meaning in regular expressions so one has to write
\.
to match an actual dot.-
Use
[^.]+
(one or more "not a dot" characters) for a class name or package component. -
Use
com\.google\.common\.[^.]+
to match any subpackage ofcom.google.common
. -
When matching concrete packages like
com.google.common
omitting the backslash before the dots may improve readability and may be just exact enough:com.google.common\.[^.]+
matches not only subpackages ofcom.google.common
but e.g. also ofcom.googleecommon
but you may not care for that. -
Do not use
.*
unless you really do not care for what is matched. Often you want to match only a certain package level instead.
Notes on static imports
Static members (including methods, constants and static inner classes) have to be explicitly allowed when they are imported, they are not automatically allowed along with their enclosing class.
For example, to allow importing both
java.util.Map
andjava.util.Map.Entry
use the following configuration:<import-control pkg="com.puppycrawl.tools.checkstyle"> <allow class="java.util.Map"/> <allow class="java.util.Map.Entry"/> </import-control>
It is also possible to use a regex with a wildcard:
<import-control pkg="com.puppycrawl.tools.checkstyle"> <allow class="java.util.Map"/> <allow class="java.util.Map.*" regex="true" /> </import-control>
Parent is
com.puppycrawl.tools.checkstyle.TreeWalker
Violation Message Keys:
-
import.control.disallowed
-
import.control.missing.file
-
import.control.unknown.pkg
- Since:
- 4.0
-
-
Nested Class Summary
-
Nested classes/interfaces inherited from class com.puppycrawl.tools.checkstyle.api.AutomaticBean
AutomaticBean.OutputStreamOptions
-
-
Field Summary
Fields Modifier and Type Field Description static java.lang.String
MSG_DISALLOWED
A key is pointing to the warning message text in "messages.properties" file.static java.lang.String
MSG_MISSING_FILE
A key is pointing to the warning message text in "messages.properties" file.static java.lang.String
MSG_UNKNOWN_PKG
A key is pointing to the warning message text in "messages.properties" file.
-
Constructor Summary
Constructors Constructor Description ImportControlCheck()
-
Method Summary
All Methods Instance Methods Concrete Methods Modifier and Type Method Description void
beginTree(DetailAST rootAST)
Called before the starting to process a tree.int[]
getAcceptableTokens()
The configurable token set.int[]
getDefaultTokens()
Returns the default token a check is interested in.java.util.Set<java.lang.String>
getExternalResourceLocations()
Returns a set of external configuration resource locations which are used by the module.int[]
getRequiredTokens()
The tokens that this check must be registered for.void
setFile(java.net.URI uri)
Setter to specify the location of the file containing the import control configuration.void
setPath(java.util.regex.Pattern pattern)
Setter to specify the regular expression of file paths to which this check should apply.void
visitToken(DetailAST ast)
Called to process a token.-
Methods inherited from class com.puppycrawl.tools.checkstyle.api.AbstractCheck
clearMessages, destroy, finishTree, getFileContents, getLine, getLines, getMessages, getTabWidth, getTokenNames, init, isCommentNodesRequired, leaveToken, log, log, log, setFileContents, setTabWidth, setTokens
-
Methods inherited from class com.puppycrawl.tools.checkstyle.api.AbstractViolationReporter
finishLocalSetup, getCustomMessages, getId, getMessageBundle, getSeverity, getSeverityLevel, setId, setSeverity
-
Methods inherited from class com.puppycrawl.tools.checkstyle.api.AutomaticBean
configure, contextualize, getConfiguration, setupChild
-
-
-
-
Field Detail
-
MSG_MISSING_FILE
public static final java.lang.String MSG_MISSING_FILE
A key is pointing to the warning message text in "messages.properties" file.- See Also:
- Constant Field Values
-
MSG_UNKNOWN_PKG
public static final java.lang.String MSG_UNKNOWN_PKG
A key is pointing to the warning message text in "messages.properties" file.- See Also:
- Constant Field Values
-
MSG_DISALLOWED
public static final java.lang.String MSG_DISALLOWED
A key is pointing to the warning message text in "messages.properties" file.- See Also:
- Constant Field Values
-
-
Method Detail
-
getDefaultTokens
public int[] getDefaultTokens()
Description copied from class:AbstractCheck
Returns the default token a check is interested in. Only used if the configuration for a check does not define the tokens.- Specified by:
getDefaultTokens
in classAbstractCheck
- Returns:
- the default tokens
- See Also:
TokenTypes
-
getAcceptableTokens
public int[] getAcceptableTokens()
Description copied from class:AbstractCheck
The configurable token set. Used to protect Checks against malicious users who specify an unacceptable token set in the configuration file. The default implementation returns the check's default tokens.- Specified by:
getAcceptableTokens
in classAbstractCheck
- Returns:
- the token set this check is designed for.
- See Also:
TokenTypes
-
getRequiredTokens
public int[] getRequiredTokens()
Description copied from class:AbstractCheck
The tokens that this check must be registered for.- Specified by:
getRequiredTokens
in classAbstractCheck
- Returns:
- the token set this must be registered for.
- See Also:
TokenTypes
-
beginTree
public void beginTree(DetailAST rootAST)
Description copied from class:AbstractCheck
Called before the starting to process a tree. Ideal place to initialize information that is to be collected whilst processing a tree.- Overrides:
beginTree
in classAbstractCheck
- Parameters:
rootAST
- the root of the tree
-
visitToken
public void visitToken(DetailAST ast)
Description copied from class:AbstractCheck
Called to process a token.- Overrides:
visitToken
in classAbstractCheck
- Parameters:
ast
- the token to process
-
getExternalResourceLocations
public java.util.Set<java.lang.String> getExternalResourceLocations()
Description copied from interface:ExternalResourceHolder
Returns a set of external configuration resource locations which are used by the module. ATTENTION! If 'getExternalResourceLocations()' return null, there will beNullPointerException
inChecker
. Such behaviour will signal that your module (check or filter) is designed incorrectly. It make sense to return an empty set from 'getExternalResourceLocations()' only for composite modules likeTreeWalker
.- Specified by:
getExternalResourceLocations
in interfaceExternalResourceHolder
- Returns:
- a set of external configuration resource locations which are used by the module.
-
setFile
public void setFile(java.net.URI uri)
Setter to specify the location of the file containing the import control configuration. It can be a regular file, URL or resource path. It will try loading the path as a URL first, then as a file, and finally as a resource.- Parameters:
uri
- the uri of the file to load.- Throws:
java.lang.IllegalArgumentException
- on error loading the file.
-
setPath
public void setPath(java.util.regex.Pattern pattern)
Setter to specify the regular expression of file paths to which this check should apply. Files that don't match the pattern will not be checked. The pattern will be matched against the full absolute file path.- Parameters:
pattern
- the file path regex this check should apply to.
-
-