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:
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">
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 is java.net.URI
.
Default value is null
.
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 is java.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 in com.puppycrawl.tools.checkstyle
except from the filters
subpackage where access to all check
's
subpackages is disallowed. Two java.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 the Files
and ClassPath
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
, and swing
). On the other hand it is not allowed to directly
access dao
and service
layer from ui
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.
Property strategyOnMismatch
is attribute of import-control
and
subpackage
tags. Property can have following values for import-control
tag:
And following values for subpackage
tags:
The following example demonstrates usage of strategyOnMismatch
property for import-control
tag. Here all imports are allowed except
java.awt.Image
and java.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 package javax.swing
and class java.io.File
.
However, any import is allowed in the classes outside of
com.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
has allowed
or disallowed
value for subpackage
tag, it makes subpackage
isolated from
parent rules. In the next example, if no matching rule was found inside
com.puppycrawl.tools.checkstyle.checks.filters
then it continues
checking in the parent subpackage, while for
com.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 from com.mycompany.dao
and any jdbc
package. In addition, only the file name named "PresentationModel"
in the package gui
are disallowed to have imports that match javax.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 except java.util.Map
inside subpackage com.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
and
java.util.stream.Collectors
are disallowed inside
com.puppycrawl.tools.checkstyle.checks.imports
package, but because of
<allow pkg="java.util.stream"/>
every import from
java.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 package sun
.
<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 the java.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.
[^.]+
(one or more "not a dot" characters) for a class name or package component.
com\.google\.common\.[^.]+
to match any subpackage of com.google.common
.
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 of com.google.common
but e.g. also of
com.googleecommon
but you may not care for that.
.*
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
and java.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
AutomaticBean.OutputStreamOptions
Modifier and Type | Field and Description |
---|---|
static String |
MSG_DISALLOWED
A key is pointing to the warning message text in "messages.properties"
file.
|
static String |
MSG_MISSING_FILE
A key is pointing to the warning message text in "messages.properties"
file.
|
static String |
MSG_UNKNOWN_PKG
A key is pointing to the warning message text in "messages.properties"
file.
|
Constructor and Description |
---|
ImportControlCheck() |
Modifier and Type | Method and 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.
|
Set<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(URI uri)
Setter to specify the location of the file containing the import control configuration.
|
void |
setPath(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.
|
clearMessages, destroy, finishTree, getFileContents, getLine, getLines, getMessages, getTabWidth, getTokenNames, init, isCommentNodesRequired, leaveToken, log, log, log, setFileContents, setTabWidth, setTokens
finishLocalSetup, getCustomMessages, getId, getMessageBundle, getSeverity, getSeverityLevel, setId, setSeverity
configure, contextualize, getConfiguration, setupChild
public static final String MSG_MISSING_FILE
public static final String MSG_UNKNOWN_PKG
public static final String MSG_DISALLOWED
public ImportControlCheck()
public int[] getDefaultTokens()
AbstractCheck
getDefaultTokens
in class AbstractCheck
TokenTypes
public int[] getAcceptableTokens()
AbstractCheck
getAcceptableTokens
in class AbstractCheck
TokenTypes
public int[] getRequiredTokens()
AbstractCheck
getRequiredTokens
in class AbstractCheck
TokenTypes
public void beginTree(DetailAST rootAST)
AbstractCheck
beginTree
in class AbstractCheck
rootAST
- the root of the treepublic void visitToken(DetailAST ast)
AbstractCheck
visitToken
in class AbstractCheck
ast
- the token to processpublic Set<String> getExternalResourceLocations()
ExternalResourceHolder
NullPointerException
in Checker
.
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 like TreeWalker
.getExternalResourceLocations
in interface ExternalResourceHolder
public void setFile(URI uri)
uri
- the uri of the file to load.IllegalArgumentException
- on error loading the file.public void setPath(Pattern pattern)
pattern
- the file path regex this check should apply to.Copyright © 2001–2020. All rights reserved.