Overview
Today I would like to share my recent work on Checkstyle at Nuxeo. In our codebase, we use Java heavily. Managing import statements for Java files is not easy, import order might be changed unintentionally. To avoid this kind of manual checking, I decided to add a new rule for Checkstyle: CustomImportOrder.
After reading this article, you will understand:
- The Different groups in “CustomImportOrder”
- Modeling import statements
- Configuring Checkstyle module
- Fixing existing import
- Other tasks to handle
If you don’t have time to read the whole article, here is the summary of the
changes in Checkstyle configuration (checkstyle.xml
):
@@ -27,6 +27,11 @@
<module name="AvoidStarImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
+ <module name="CustomImportOrder">
+ <property name="customImportOrderRules" value="STATIC###STANDARD_JAVA_PACKAGE###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE"/>
+ <property name="specialImportsRegExp" value="^org\."/>
+ </module>
<!-- Miscellaneous -->
<module name="ArrayTypeStyle"/>
Import Groups
According to Checkstyle documentation Checkstyle CustomImportOrder > Rule Description, the rule consists 5 groups: STATIC, SAME_PACKAGE(n), THIRD_PARTY_PACKAGE, STANDARD_JAVA_PACKAGE, and SPECIAL_IMPORTS.
STATIC group. This group sets the ordering of static imports.
SAME_PACKAGE(n) group. This group sets the ordering of the same package imports. Imports are considered on SAME_PACKAGE group if n first domains in package name and import name are identical:
package java.util.concurrent.locks;
import java.io.File;
import java.util.*; //#1
import java.util.List; //#2
import java.util.StringTokenizer; //#3
import java.util.concurrent.*; //#4
import java.util.concurrent.AbstractExecutorService; //#5
import java.util.concurrent.locks.LockSupport; //#6
import java.util.regex.Pattern; //#7
import java.util.regex.Matcher; //#8
If we have SAME_PACKAGE(3) on configuration file, imports #4-6
will be
considered as a SAME_PACKAGE group (java.util.concurrent.*,
java.util.concurrent.AbstractExecutorService,
java.util.concurrent.locks.LockSupport). SAME_PACKAGE(2) will include #1-8
.
SAME_PACKAGE(4) will include only #6
. SAME_PACKAGE(5) will result in no
imports assigned to SAME_PACKAGE group because actual package
java.util.concurrent.locks has only 4 domains.
THIRD_PARTY_PACKAGE group. This group sets ordering of third party imports. Third-party imports are all imports except STATIC, SAME_PACKAGE(n), STANDARD_JAVA_PACKAGE and SPECIAL_IMPORTS.
STANDARD_JAVA_PACKAGE group. By default, this group sets ordering of standard java/javax imports.
SPECIAL_IMPORTS group. This group may contain some imports that have particular meaning for the user.
Modeling Import Statements
Before applying the Checkstyle module CustomImportOrder, I have to understand how
Nuxeo import statements work. Nuxeo import order is defined by file
nuxeo.importorder
:
#Organize Import Order
#Thu Dec 04 14:57:39 CET 2014
3=com
2=org
1=javax
0=java
It means Java (java
) and Java EE (javax
) import statements go first; then
non-profitable organization (org
) go next; finally, the company’s statements go
last (com
). It is worth to mention that static statements are not part of this
order. In IntelliJ IDEA, static statements go before Java statements, so I just
followed the existing rule, thus considered it as the order “-1”.
package com.nuxeo.pkg;
import static com.nuxeo.pkg.Constants.A;
import static com.nuxeo.pkg.Constants.B;
import static com.nuxeo.pkg.Constants.C;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
import org.apache.commons.io.FileUtils;
import com.nuxeo.pkg.ClassA;
import com.nuxeo.pkg.ClassB;
import com.nuxeo.pkg.ClassC;
As you can see, static statements match Checkstyle group STATIC; Java and Java
EE statements match Checkstyle group STANDARD_JAVA_PACKAGE, defined
by the regular expression: ^(java|javax)\.
. For the remaining ones, there are only
org.*
and com.*
. But how to match them with groups? My first feeling is to
avoid SAME_PACKAGE(n) group, because there is no distinction for packages
starting with the same package name as the current one. So SAME_PACKAGE(n) is
not an option. Then, I went to THIRD_PARTY_PACKAGE: since this group contains
all statements not being included by other groups, it can fit our need! Make
org.*
goes to SPECIAL_IMPORT group, then com.*
can be considered as others.
Therefore, the import groups are modelized as:
- STATIC
- STANDARD_JAVA_PACKAGE
- SPECIAL_IMPORT
- THIRD_PARTY_PACKAGE
Configuring Checkstyle
Based on the result above, the Checkstyle module can be configured with property
customImportOrderRules
containing the 4 groups, with ###
as the separator:
<property
name="customImportOrderRules"
value="STATIC###STANDARD_JAVA_PACKAGE###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE" />
Combined with special imports regular expression:
<property
name="specialImportsRegExp"
value="^org\." />
and all remaining properties can be kept as default. They don’t have to be changed.
The final diff in checkstyle.xml is:
@@ -27,6 +27,11 @@
<module name="AvoidStarImport"/>
<module name="RedundantImport"/>
<module name="UnusedImports"/>
+ <module name="CustomImportOrder">
+ <property name="customImportOrderRules" value="STATIC###STANDARD_JAVA_PACKAGE###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE"/>
+ <property name="specialImportsRegExp" value="^org\."/>
+ </module>
<!-- Miscellaneous -->
<module name="ArrayTypeStyle"/>
Fix Existing Imports
Once done, I had to fix all existing import statements. I launched the following Maven command to check them manually:
$ mvn checkstyle:check
They might also be fixed using IDE auto-format tool. For example, in IntelliJ IDEA, you can select the root directory in Project view, right-click and choose either “Reformat Code” or “Optimize Imports” to do that.
Other Tasks
After the fix, I have to notify teammates about the changes, also ensure their
IDE settings have the custom import order defined by our own file
nuxeo.importorder
. This step is very important because teammates might not
aware of such changes.
Conclusion
Today, I shared my work experience about adding CustomImportOrder into existing checkstyle rules for Maven project. We saw the 5 import groups for static, same-package, third-party, standard Java, and special imports.
By the way, I sent some pull-requests to the Checkstyle project recently. You can see my contributions here: https://github.com/checkstyle/checkstyle/commits?author=mincong-h. Thanks to them, I believe I will be able to share more with you in the future, related to Checkstyle or code-quality in general. Hope you enjoy this article, see you the next time!
References
- Checkstyle, “CustomImportOrder”, Checkstyle, 2019. https://checkstyle.org/config_imports.html#CustomImportOrder