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" 055 * file. 056 */ 057 public static final String MSG_KEY = "import.avoidModule"; 058 059 /** 060 * A key is pointing to the warning message text in "messages.properties" 061 * file. 062 */ 063 public static final String MSG_COUNT = "import.avoidModuleCount"; 064 065 /** 066 * Specify module names for which {@code import module} declarations are allowed. 067 */ 068 private final Set<String> excludes = new HashSet<>(); 069 070 /** 071 * Maximum number of allowed module imports. 072 */ 073 private int maxAllowedModuleImports; 074 075 /** 076 * Counter for used module imports. 077 */ 078 private int currentModuleImportsCount; 079 080 /** 081 * Creates a new {@code AvoidModuleImportCheck} instance. 082 */ 083 public AvoidModuleImportCheck() { 084 // no code by default 085 } 086 087 @Override 088 public int[] getDefaultTokens() { 089 return getRequiredTokens(); 090 } 091 092 @Override 093 public int[] getAcceptableTokens() { 094 return new int[] { 095 TokenTypes.MODULE_IMPORT, 096 }; 097 } 098 099 @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}