001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.imports; 021 022import java.util.Arrays; 023import java.util.HashSet; 024import java.util.Set; 025 026import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 027import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 028import com.puppycrawl.tools.checkstyle.api.DetailAST; 029import com.puppycrawl.tools.checkstyle.api.FullIdent; 030import com.puppycrawl.tools.checkstyle.api.TokenTypes; 031import com.puppycrawl.tools.checkstyle.utils.NullUtil; 032 033/** 034 * <div> 035 * Checks that there are no module imports. 036 * </div> 037 * 038 * <p> 039 * Rationale: Module import declarations ({@code import module M;}) import, on 040 * demand, every public top level type exported by a module and by any 041 * modules it transitively reads. This is a much broader, less explicit 042 * surface than single type or on demand package imports, making it harder to 043 * tell where a type comes from, and it increases the risk of ambiguous 044 * references between same named types in different exported packages. 045 * Disallowing module imports keeps imports explicit and predictable. 046 * </p> 047 * 048 * @since 14.1.0 049 */ 050@FileStatefulCheck 051public class AvoidModuleImportCheck extends AbstractCheck { 052 053 /** 054 * A key is pointing to the warning message text in "messages.properties" file. 055 */ 056 public static final String MSG_KEY = "import.avoidModule"; 057 058 /** 059 * A key is pointing to the warning message text in "messages.properties" file. 060 */ 061 public static final String MSG_COUNT = "import.avoidModuleCount"; 062 063 /** 064 * Specify module names for which {@code import module} declarations are allowed. 065 */ 066 private final Set<String> excludes = new HashSet<>(); 067 068 /** 069 * Maximum number of allowed module imports. 070 */ 071 private int maxAllowedModuleImports; 072 073 /** 074 * Counter for used module imports. 075 */ 076 private int currentModuleImportsCount; 077 078 /** 079 * Creates a new {@code AvoidModuleImportCheck} instance. 080 */ 081 public AvoidModuleImportCheck() { 082 // no code by default 083 } 084 085 @Override 086 public int[] getDefaultTokens() { 087 return getRequiredTokens(); 088 } 089 090 @Override 091 public int[] getAcceptableTokens() { 092 return new int[] { 093 TokenTypes.MODULE_IMPORT, 094 }; 095 } 096 097 @Override 098 public int[] getRequiredTokens() { 099 return getAcceptableTokens(); 100 } 101 102 /** 103 * Setter to specify modules allowed to import. 104 * 105 * @param excludesParam module names 106 * @since 14.1.0 107 */ 108 public void setExcludes(String... excludesParam) { 109 excludes.addAll(Arrays.asList(excludesParam)); 110 } 111 112 /** 113 * Setter to control number of module imports allowed. 114 * 115 * @param count the number of module imports allowed 116 * @since 14.1.0 117 */ 118 public void setMaxAllowedModuleImports(int count) { 119 maxAllowedModuleImports = count; 120 } 121 122 @Override 123 public void beginTree(DetailAST rootAST) { 124 currentModuleImportsCount = 0; 125 } 126 127 @Override 128 public void visitToken(DetailAST ast) { 129 currentModuleImportsCount++; 130 final DetailAST module = NullUtil.notNull(ast.getFirstChild()); 131 final DetailAST startingDot = NullUtil.notNull(module.getNextSibling()); 132 final String name = FullIdent.createFullIdent(startingDot).getText(); 133 if (currentModuleImportsCount > maxAllowedModuleImports 134 && !excludes.contains(name)) { 135 if (maxAllowedModuleImports > 0) { 136 log(ast, MSG_COUNT, maxAllowedModuleImports); 137 } 138 else { 139 log(ast, MSG_KEY, name); 140 } 141 } 142 } 143 144}