Class ImportOrderCheck

  • All Implemented Interfaces:
    Configurable, Contextualizable

    public class ImportOrderCheck
    extends AbstractCheck

    Checks the ordering/grouping of imports. Features are:

    • groups type/static imports: ensures that groups of imports come in a specific order (e.g., java. comes first, javax. comes second, then everything else)
    • adds a separation between type import groups : ensures that a blank line sit between each group
    • type/static import groups aren't separated internally: ensures that each group aren't separated internally by blank line or comment
    • sorts type/static imports inside each group: ensures that imports within each group are in lexicographic order
    • sorts according to case: ensures that the comparison between imports is case sensitive, in ASCII sort order
    • arrange static imports: ensures the relative order between type imports and static imports (see ImportOrderOption)
    • Property option - specify policy on the relative order between type imports and static imports. Type is com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderOption. Default value is under.
    • Property groups - specify list of type import groups (every group identified either by a common prefix string, or by a regular expression enclosed in forward slashes (e.g. /regexp/). All type imports, which does not match any group, falls into an additional group, located at the end. Thus, the empty list of type groups (the default value) means one group for all type imports. Type is java.util.regex.Pattern[]. Default value is "".
    • Property ordered - control whether type imports within each group should be sorted. It doesn't affect sorting for static imports. Type is boolean. Default value is true.
    • Property separated - control whether type import groups should be separated by, at least, one blank line or comment and aren't separated internally. It doesn't affect separations for static imports. Type is boolean. Default value is false.
    • Property separatedStaticGroups - control whether static import groups should be separated by, at least, one blank line or comment and aren't separated internally. This property has effect only when the property option is set to top or bottom and when property staticGroups is enabled. Type is boolean. Default value is false.
    • Property caseSensitive - control whether string comparison should be case sensitive or not. Case sensitive sorting is in ASCII sort order. It affects both type imports and static imports. Type is boolean. Default value is true.
    • Property staticGroups - specify list of static import groups (every group identified either by a common prefix string, or by a regular expression enclosed in forward slashes (e.g. /regexp/). All static imports, which does not match any group, falls into an additional group, located at the end. Thus, the empty list of static groups (the default value) means one group for all static imports. This property has effect only when the property option is set to top or bottom. Type is java.util.regex.Pattern[]. Default value is "".
    • Property sortStaticImportsAlphabetically - control whether static imports located at top or bottom are sorted within the group. Type is boolean. Default value is false.
    • Property useContainerOrderingForStatic - control whether to use container ordering (Eclipse IDE term) for static imports or not. Type is boolean. Default value is false.
    • Property tokens - tokens to check Type is java.lang.String[]. Validation type is tokenSet. Default value is: STATIC_IMPORT.

    To configure the check:

     <module name="ImportOrder"/>
     

    Example:

     import java.io.IOException;
     import java.net.URL;
    
     import java.io.IOException; // violation, extra separation before import
                                 // and wrong order, comes before 'java.net.URL'.
     import javax.net.ssl.TrustManager; // violation, extra separation due to above comment
     import javax.swing.JComponent;
     import org.apache.http.conn.ClientConnectionManager; // OK
     import java.util.Set; // violation, wrong order, 'java' should not come after 'org' imports
     import com.neurologic.http.HttpClient; // violation, wrong order, 'com' imports comes at top
     import com.neurologic.http.impl.ApacheHttpClient; // OK
    
     public class SomeClass { }
     

    To configure the check so that it matches default Eclipse formatter configuration (tested on Kepler and Luna releases):

    • group of static imports is on the top
    • groups of type imports: "java" and "javax" packages first, then "org" and then all other imports
    • imports will be sorted in the groups
    • groups are separated by, at least, one blank line and aren't separated internally

    Notes:

    • "com" package is not mentioned on configuration, because it is ignored by Eclipse Kepler and Luna (looks like Eclipse defect)
    • configuration below doesn't work in all 100% cases due to inconsistent behavior prior to Mars release, but covers most scenarios
     <module name="ImportOrder">
       <property name="groups" value="/^java\./,javax,org"/>
       <property name="ordered" value="true"/>
       <property name="separated" value="true"/>
       <property name="option" value="above"/>
       <property name="sortStaticImportsAlphabetically" value="true"/>
     </module>
     

    Example:

     import static java.lang.System.out;
     import static java.lang.Math; // violation, alphabetical case sensitive ASCII order, 'M' < 'S'
     import java.io.IOException;
    
     import java.net.URL; // violation, extra separation before import
     import java.security.KeyManagementException;
    
     import javax.net.ssl.TrustManager;
    
     import javax.net.ssl.X509TrustManager; // violation, groups should not be separated internally
    
     import org.apache.http.conn.ClientConnectionManager;
    
     public class SomeClass { }
     

    To configure the check so that it matches default Eclipse formatter configuration (tested on Mars release):

    • group of static imports is on the top
    • groups of type imports: "java" and "javax" packages first, then "org" and "com", then all other imports as one group
    • imports will be sorted in the groups
    • groups are separated by, at least, one blank line and aren't separated internally
     <module name="ImportOrder">
       <property name="groups" value="/^java\./,javax,org,com"/>
       <property name="ordered" value="true"/>
       <property name="separated" value="true"/>
       <property name="option" value="above"/>
       <property name="sortStaticImportsAlphabetically" value="true"/>
     </module>
     

    Example:

     import static java.io.File.createTempFile;
     import static java.lang.Math.abs; // OK, alphabetical case sensitive ASCII order, 'i' < 'l'
     import java.lang.Math.sqrt; // OK, follows property 'Option' value 'above'
     import java.io.File; // violation, alphabetical case sensitive ASCII order, 'i' < 'l'
    
     import java.io.IOException; // violation, extra separation in 'java' import group
    
     import org.albedo.*;
    
     import static javax.WindowConstants.*; // violation, wrong order, 'javax' comes before 'org'
     import javax.swing.JComponent;
     import org.apache.http.ClientConnectionManager; // violation, must separate from previous import
     import org.linux.apache.server.SoapServer; // OK
    
     import com.neurologic.http.HttpClient; // OK
     import com.neurologic.http.impl.ApacheHttpClient; // OK
    
     public class SomeClass { }
     

    To configure the check so that it matches default IntelliJ IDEA formatter configuration (tested on v2018.2):

    • group of static imports is on the bottom
    • groups of type imports: all imports except of "javax" and "java", then "javax" and "java"
    • imports will be sorted in the groups
    • groups are separated by, at least, one blank line and aren't separated internally

    Note: a suppression xpath single filter is needed because IDEA has no blank line between "javax" and "java". ImportOrder has a limitation by design to enforce an empty line between groups ("java", "javax"). There is no flexibility to enforce empty lines between some groups and no empty lines between other groups.

    Note: "separated" option is disabled because IDEA default has blank line between "java" and static imports, and no blank line between "javax" and "java".

     <module name="ImportOrder">
       <property name="groups" value="*,javax,java"/>
       <property name="ordered" value="true"/>
       <property name="separated" value="false"/>
       <property name="option" value="bottom"/>
       <property name="sortStaticImportsAlphabetically" value="true"/>
     </module>
     <module name="SuppressionXpathSingleFilter">
       <property name="checks" value="ImportOrder"/>
       <property name="message" value="^'java\..*'.*"/>
     </module>
     

    Example:

     import com.neurologic.http.impl.ApacheHttpClient; // OK
     import static java.awt.Button.A;
     import javax.swing.JComponent; // violation, wrong order, caused by above static import
                                    // all static imports comes at bottom
     import java.net.URL; // violation, extra separation in import group
     import java.security.KeyManagementException;
     import javax.swing.JComponent; // violation, wrong order, 'javax' should be above 'java' imports
     import com.neurologic.http.HttpClient; // violation, wrong order, 'com' imports should be at top
    
     public class TestClass { }
     

    To configure the check so that it matches default NetBeans formatter configuration (tested on v8):

    • groups of type imports are not defined, all imports will be sorted as a one group
    • static imports are not separated, they will be sorted along with other imports
     <module name="ImportOrder">
       <property name="option" value="inflow"/>
     </module>
     

    Example:

     import static java.io.File.createTempFile;
     import java.lang.Math.sqrt;
    
     import javax.swing.JComponent; // violation, extra separation in import group
     import static javax.windowConstants.*; // OK
                                         // all static imports are processed like non static imports.
     public class SomeClass { }
     

    Group descriptions enclosed in slashes are interpreted as regular expressions. If multiple groups match, the one matching a longer substring of the imported name will take precedence, with ties broken first in favor of earlier matches and finally in favor of the first matching group.

    There is always a wildcard group to which everything not in a named group belongs. If an import does not match a named group, the group belongs to this wildcard group. The wildcard group position can be specified using the * character.

    Check also has on option making it more flexible: sortStaticImportsAlphabetically - sets whether static imports grouped by top or bottom option should be sorted alphabetically or not, default value is false. It is applied to static imports grouped with top or bottom options. This option is helping in reconciling of this Check and other tools like Eclipse's Organize Imports feature.

    To configure the Check allows static imports grouped to the top being sorted alphabetically:

     <module name="ImportOrder">
       <property name="sortStaticImportsAlphabetically" value="true"/>
       <property name="option" value="top"/>
     </module>
     
     import static java.lang.Math.PI;
     import static java.lang.Math.abs; // OK, alphabetical case sensitive ASCII order, 'P' < 'a'
     import static org.abego.treelayout.Configuration.AlignmentInLevel; // OK, alphabetical order
    
     import java.util.Set; // violation, extra separation in import group
     import static java.lang.Math.abs; // violation, wrong order, all static imports comes at 'top'
     import org.abego.*;
    
     public class SomeClass { }
     

    To configure the Check with groups of static imports:

     <module name="ImportOrder">
       <property name="staticGroups" value="org,java"/>
       <property name="sortStaticImportsAlphabetically" value="true"/>
       <property name="option" value="top"/>
     </module>
     
     import static org.abego.treelayout.Configuration.AlignmentInLevel; // Group 1
     import static java.lang.Math.abs; // Group 2
     import static java.lang.String.format; // Group 2
     import static com.google.common.primitives.Doubles.BYTES; // Group "everything else"
    
     public class SomeClass { }
     

    The following example shows the idea of 'useContainerOrderingForStatic' option that is useful for Eclipse IDE users to match ordering validation. This is how the import comparison works for static imports: we first compare the container of the static import, container is the type enclosing the static element being imported. When the result of the comparison is 0 (containers are equal), we compare the fully qualified import names. For e.g. this is what is considered to be container names for the given example: import static HttpConstants.COLON => HttpConstants import static HttpHeaders.addHeader => HttpHeaders import static HttpHeaders.setHeader => HttpHeaders import static HttpHeaders.Names.DATE => HttpHeaders.Names According to this logic, HttpHeaders.Names should come after HttpHeaders.

    Example for useContainerOrderingForStatic=true

     <module name="ImportOrder">
       <property name="useContainerOrderingForStatic" value="true"/>
       <property name="ordered" value="true"/>
       <property name="option" value="top"/>
       <property name="caseSensitive" value="false"/>
       <property name="sortStaticImportsAlphabetically" value="true"/>
     </module>
     
     import static io.netty.handler.codec.http.HttpConstants.COLON;
     import static io.netty.handler.codec.http.HttpHeaders.addHeader;
     import static io.netty.handler.codec.http.HttpHeaders.setHeader;
     import static io.netty.handler.codec.http.HttpHeaders.Names.DATE;
    
     public class InputEclipseStaticImportsOrder { }
     

    Example for useContainerOrderingForStatic=false

     <module name="ImportOrder">
       <property name="useContainerOrderingForStatic" value="false"/>
       <property name="ordered" value="true"/>
       <property name="option" value="top"/>
       <property name="caseSensitive" value="false"/>
       <property name="sortStaticImportsAlphabetically" value="true"/>
     </module>
     
     import static io.netty.handler.codec.http.HttpConstants.COLON;
     import static io.netty.handler.codec.http.HttpHeaders.addHeader;
     import static io.netty.handler.codec.http.HttpHeaders.setHeader;
     import static io.netty.handler.codec.http.HttpHeaders.Names.DATE; // violation
    
     public class InputEclipseStaticImportsOrder { }
     

    To configure the check to enforce static import group separation

    Example for separatedStaticGroups=true

     <module name="ImportOrder">
       <property name="staticGroups" value="*,java,javax,org"/>
       <property name="option" value="top"/>
       <property name="separatedStaticGroups" value="true"/>
     </module>
     
     import static java.lang.Math.PI;
     import static java.io.File.createTempFile;
     import static javax.swing.JComponent; // violation, should be separated from previous imports
     import static javax.WindowConstants.*; // OK
    
     import java.net.URL;
    
     public class SomeClass { }
     

    To configure the Check with groups of static imports when staticGroups="" represents all imports as everything else group:

     <module name="ImportOrder">
       <property name="staticGroups" value=""/>
       <property name="option" value="top"/>
     </module>
     
     import static java.io.File.listRoots;   // OK
     import static javax.swing.WindowConstants.*; // OK
     import static java.io.File.createTempFile; // OK
     import static com.puppycrawl.tools.checkstyle;  // OK
    
     public class SomeClass { }
     

    To configure the Check with groups of static imports when staticGroups="java, javax" represents three groups i.e java*, javax* and * (everything else). In below example the static imports com... should be in last group:

     <module name="ImportOrder">
       <property name="staticGroups" value="java, javax"/>
       <property name="option" value="top"/>
     </module>
     
     import static java.io.File.listRoots;   // OK
     import static javax.swing.WindowConstants.*; // OK
     import static java.io.File.createTempFile; // violation should be before javax
     import static com.puppycrawl.tools.checkstyle;  // OK
    
     public class SomeClass { }
     

    Parent is com.puppycrawl.tools.checkstyle.TreeWalker

    Violation Message Keys:

    • import.groups.separated.internally
    • import.ordering
    • import.separation
    Since:
    3.2
    • Field Detail

      • groups

        private Pattern[] groups
        Specify list of type import groups (every group identified either by a common prefix string, or by a regular expression enclosed in forward slashes (e.g. /regexp/). All type imports, which does not match any group, falls into an additional group, located at the end. Thus, the empty list of type groups (the default value) means one group for all type imports.
      • staticGroups

        private Pattern[] staticGroups
        Specify list of static import groups (every group identified either by a common prefix string, or by a regular expression enclosed in forward slashes (e.g. /regexp/). All static imports, which does not match any group, falls into an additional group, located at the end. Thus, the empty list of static groups (the default value) means one group for all static imports. This property has effect only when the property option is set to top or bottom.
      • separated

        private boolean separated
        Control whether type import groups should be separated by, at least, one blank line or comment and aren't separated internally. It doesn't affect separations for static imports.
      • separatedStaticGroups

        private boolean separatedStaticGroups
        Control whether static import groups should be separated by, at least, one blank line or comment and aren't separated internally. This property has effect only when the property option is set to top or bottom and when property staticGroups is enabled.
      • ordered

        private boolean ordered
        Control whether type imports within each group should be sorted. It doesn't affect sorting for static imports.
      • caseSensitive

        private boolean caseSensitive
        Control whether string comparison should be case sensitive or not. Case sensitive sorting is in ASCII sort order. It affects both type imports and static imports.
      • lastGroup

        private int lastGroup
        Last imported group.
      • lastImportLine

        private int lastImportLine
        Line number of last import.
      • beforeFirstImport

        private boolean beforeFirstImport
        Whether there was any imports.
      • staticImportsApart

        private boolean staticImportsApart
        Whether static and type import groups should be split apart. When the option property is set to INFLOW, ABOVE or UNDER, both the type and static imports use the properties groups and separated. When the option property is set to TOP or BOTTOM, static imports uses the properties staticGroups and separatedStaticGroups.
      • sortStaticImportsAlphabetically

        private boolean sortStaticImportsAlphabetically
        Control whether static imports located at top or bottom are sorted within the group.
      • useContainerOrderingForStatic

        private boolean useContainerOrderingForStatic
        Control whether to use container ordering (Eclipse IDE term) for static imports or not.
      • option

        private ImportOrderOption option
        Specify policy on the relative order between type imports and static imports.
    • Method Detail

      • setOption

        public void setOption​(String optionStr)
        Setter to specify policy on the relative order between type imports and static imports.
        Parameters:
        optionStr - string to decode option from
        Throws:
        IllegalArgumentException - if unable to decode
      • setGroups

        public void setGroups​(String... packageGroups)
        Setter to specify list of type import groups (every group identified either by a common prefix string, or by a regular expression enclosed in forward slashes (e.g. /regexp/). All type imports, which does not match any group, falls into an additional group, located at the end. Thus, the empty list of type groups (the default value) means one group for all type imports.
        Parameters:
        packageGroups - a comma-separated list of package names/prefixes.
      • setStaticGroups

        public void setStaticGroups​(String... packageGroups)
        Setter to specify list of static import groups (every group identified either by a common prefix string, or by a regular expression enclosed in forward slashes (e.g. /regexp/). All static imports, which does not match any group, falls into an additional group, located at the end. Thus, the empty list of static groups (the default value) means one group for all static imports. This property has effect only when the property option is set to top or bottom.
        Parameters:
        packageGroups - a comma-separated list of package names/prefixes.
      • setOrdered

        public void setOrdered​(boolean ordered)
        Setter to control whether type imports within each group should be sorted. It doesn't affect sorting for static imports.
        Parameters:
        ordered - whether lexicographic ordering of imports within a group required or not.
      • setSeparated

        public void setSeparated​(boolean separated)
        Setter to control whether type import groups should be separated by, at least, one blank line or comment and aren't separated internally. It doesn't affect separations for static imports.
        Parameters:
        separated - whether groups should be separated by one blank line or comment.
      • setSeparatedStaticGroups

        public void setSeparatedStaticGroups​(boolean separatedStaticGroups)
        Setter to control whether static import groups should be separated by, at least, one blank line or comment and aren't separated internally. This property has effect only when the property option is set to top or bottom and when property staticGroups is enabled.
        Parameters:
        separatedStaticGroups - whether groups should be separated by one blank line or comment.
      • setCaseSensitive

        public void setCaseSensitive​(boolean caseSensitive)
        Setter to control whether string comparison should be case sensitive or not. Case sensitive sorting is in ASCII sort order. It affects both type imports and static imports.
        Parameters:
        caseSensitive - whether string comparison should be case sensitive.
      • setSortStaticImportsAlphabetically

        public void setSortStaticImportsAlphabetically​(boolean sortAlphabetically)
        Setter to control whether static imports located at top or bottom are sorted within the group.
        Parameters:
        sortAlphabetically - true or false.
      • setUseContainerOrderingForStatic

        public void setUseContainerOrderingForStatic​(boolean useContainerOrdering)
        Setter to control whether to use container ordering (Eclipse IDE term) for static imports or not.
        Parameters:
        useContainerOrdering - whether to use container ordering for static imports or not.
      • 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 class AbstractCheck
        Returns:
        the token set this check is designed 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 class AbstractCheck
        Parameters:
        rootAST - the root of the tree
      • doVisitToken

        private void doVisitToken​(FullIdent ident,
                                  boolean isStatic,
                                  boolean previous,
                                  DetailAST ast)
        Shares processing...
        Parameters:
        ident - the import to process.
        isStatic - whether the token is static or not.
        previous - previous non-static but current is static (above), or previous static but current is non-static (under).
        ast - node of the AST.
      • needSeparator

        private boolean needSeparator​(boolean isStatic)
        Checks whether import groups should be separated.
        Parameters:
        isStatic - whether the token is static or not.
        Returns:
        true if imports groups should be separated.
      • isSeparatorInGroup

        private boolean isSeparatorInGroup​(int groupIdx,
                                           boolean isStatic,
                                           int line)
        Checks whether imports group separated internally.
        Parameters:
        groupIdx - group number.
        isStatic - whether the token is static or not.
        line - the line of the current import.
        Returns:
        true if imports group are separated internally.
      • isSeparatorBeforeImport

        private boolean isSeparatorBeforeImport​(int line)
        Checks whether there is any separator before current import.
        Parameters:
        line - the line of the current import.
        Returns:
        true if there is separator before current import which isn't the first import.
      • doVisitTokenInSameGroup

        private void doVisitTokenInSameGroup​(boolean isStatic,
                                             boolean previous,
                                             String name,
                                             DetailAST ast)
        Shares processing...
        Parameters:
        isStatic - whether the token is static or not.
        previous - previous non-static but current is static (above), or previous static but current is non-static (under).
        name - the name of the current import.
        ast - node of the AST.
      • isWrongOrder

        private boolean isWrongOrder​(String name,
                                     boolean isStatic)
        Checks whether import name is in wrong order.
        Parameters:
        name - import name.
        isStatic - whether it is a static import name.
        Returns:
        true if import name is in wrong order.
      • compareContainerOrder

        private static int compareContainerOrder​(String importName1,
                                                 String importName2,
                                                 boolean caseSensitive)
        Compares two import strings. We first compare the container of the static import, container being the type enclosing the static element being imported. When this returns 0, we compare the qualified import name. For e.g. this is what is considered to be container names:
         import static HttpConstants.COLON     => HttpConstants
         import static HttpHeaders.addHeader   => HttpHeaders
         import static HttpHeaders.setHeader   => HttpHeaders
         import static HttpHeaders.Names.DATE  => HttpHeaders.Names
         

        According to this logic, HttpHeaders.Names would come after HttpHeaders. For more details, see static imports comparison method in Eclipse.

        Parameters:
        importName1 - first import name.
        importName2 - second import name.
        caseSensitive - whether the comparison of fully qualified import names is case sensitive.
        Returns:
        the value 0 if str1 is equal to str2; a value less than 0 if str is less than the str2 (container order or lexicographical); and a value greater than 0 if str1 is greater than str2 (container order or lexicographically).
      • getImportContainer

        private static String getImportContainer​(String qualifiedImportName)
        Extracts import container name from fully qualified import name. An import container name is the type which encloses the static element being imported. For example, HttpConstants, HttpHeaders, HttpHeaders.Names are import container names:
         import static HttpConstants.COLON     => HttpConstants
         import static HttpHeaders.addHeader   => HttpHeaders
         import static HttpHeaders.setHeader   => HttpHeaders
         import static HttpHeaders.Names.DATE  => HttpHeaders.Names
         
        Parameters:
        qualifiedImportName - fully qualified import name.
        Returns:
        import container name.
      • getGroupNumber

        private int getGroupNumber​(boolean isStatic,
                                   String name)
        Finds out what group the specified import belongs to.
        Parameters:
        isStatic - whether the token is static or not.
        name - the import name to find.
        Returns:
        group number for given import name.
      • getGroupNumber

        private static int getGroupNumber​(Pattern[] patterns,
                                          String name)
        Finds out what group the specified import belongs to.
        Parameters:
        patterns - groups to check.
        name - the import name to find.
        Returns:
        group number for given import name.
      • compare

        private static int compare​(String string1,
                                   String string2,
                                   boolean caseSensitive)
        Compares two strings.
        Parameters:
        string1 - the first string.
        string2 - the second string.
        caseSensitive - whether the comparison is case sensitive.
        Returns:
        the value 0 if string1 is equal to string2; a value less than 0 if string1 is lexicographically less than the string2; and a value greater than 0 if string1 is lexicographically greater than string2.
      • compilePatterns

        private static Pattern[] compilePatterns​(String... packageGroups)
        Compiles the list of package groups and the order they should occur in the file.
        Parameters:
        packageGroups - a comma-separated list of package names/prefixes.
        Returns:
        array of compiled patterns.
        Throws:
        IllegalArgumentException - if any of the package groups are not valid.