View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.imports;
21  
22  import java.util.Arrays;
23  import java.util.HashSet;
24  import java.util.Set;
25  
26  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
27  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28  import com.puppycrawl.tools.checkstyle.api.DetailAST;
29  import com.puppycrawl.tools.checkstyle.api.FullIdent;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.NullUtil;
32  
33  /**
34   * <div>
35   * Checks that there are no module imports.
36   * </div>
37   *
38   * <p>
39   * Rationale: Module import declarations ({@code import module M;}) import, on
40   * demand, every public top level type exported by a module and by any
41   * modules it transitively reads. This is a much broader, less explicit
42   * surface than single type or on demand package imports, making it harder to
43   * tell where a type comes from, and it increases the risk of ambiguous
44   * references between same named types in different exported packages.
45   * Disallowing module imports keeps imports explicit and predictable.
46   * </p>
47   *
48   * @since 14.1.0
49   */
50  @FileStatefulCheck
51  public class AvoidModuleImportCheck extends AbstractCheck {
52  
53      /**
54       * A key is pointing to the warning message text in "messages.properties"
55       * file.
56       */
57      public static final String MSG_KEY = "import.avoidModule";
58  
59      /**
60       * A key is pointing to the warning message text in "messages.properties"
61       * file.
62       */
63      public static final String MSG_COUNT = "import.avoidModuleCount";
64  
65      /**
66       * Specify module names for which {@code import module} declarations are allowed.
67       */
68      private final Set<String> excludes = new HashSet<>();
69  
70      /**
71       * Maximum number of allowed module imports.
72       */
73      private int maxAllowedModuleImports;
74  
75      /**
76       * Counter for used module imports.
77       */
78      private int currentModuleImportsCount;
79  
80      /**
81       * Creates a new {@code AvoidModuleImportCheck} instance.
82       */
83      public AvoidModuleImportCheck() {
84          // no code by default
85      }
86  
87      @Override
88      public int[] getDefaultTokens() {
89          return getRequiredTokens();
90      }
91  
92      @Override
93      public int[] getAcceptableTokens() {
94          return new int[] {
95              TokenTypes.MODULE_IMPORT,
96          };
97      }
98  
99      @Override
100     public int[] getRequiredTokens() {
101         return getAcceptableTokens();
102     }
103 
104     /**
105      * Setter to specify modules allowed to import.
106      *
107      * @param excludesParam module names
108      * @since 14.1.0
109      */
110     public void setExcludes(String... excludesParam) {
111         excludes.addAll(Arrays.asList(excludesParam));
112     }
113 
114     /**
115      * Setter to control number of module imports allowed.
116      *
117      * @param count the number of module imports allowed
118      * @since 14.1.0
119      */
120     public void setMaxAllowedModuleImports(int count) {
121         maxAllowedModuleImports = count;
122     }
123 
124     @Override
125     public void beginTree(DetailAST rootAST) {
126         currentModuleImportsCount = 0;
127     }
128 
129     @Override
130     public void visitToken(DetailAST ast) {
131         currentModuleImportsCount++;
132         final DetailAST module = NullUtil.notNull(ast.getFirstChild());
133         final DetailAST startingDot = NullUtil.notNull(module.getNextSibling());
134         final String name = FullIdent.createFullIdent(startingDot).getText();
135         if (currentModuleImportsCount > maxAllowedModuleImports
136                 && !excludes.contains(name)) {
137             if (maxAllowedModuleImports > 0) {
138                 log(ast, MSG_COUNT, maxAllowedModuleImports);
139             }
140             else {
141                 log(ast, MSG_KEY, name);
142             }
143         }
144     }
145 
146 }