1 /////////////////////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules. 3 // Copyright (C) 2001-2024 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.Deque; 23 import java.util.LinkedList; 24 25 /** 26 * Represents a tree of import rules for controlling whether packages or 27 * classes are allowed to be used. Each instance must have a single parent or 28 * be the root node. 29 */ 30 abstract class AbstractImportControl { 31 32 /** List of {@link AbstractImportRule} objects to check. */ 33 private final Deque<AbstractImportRule> rules = new LinkedList<>(); 34 /** The parent. Null indicates we are the root node. */ 35 private final AbstractImportControl parent; 36 /** Strategy in a case if matching allow/disallow rule was not found. */ 37 private final MismatchStrategy strategyOnMismatch; 38 39 /** 40 * Construct a child node. 41 * 42 * @param parent the parent node. 43 * @param strategyOnMismatch strategy in a case if matching allow/disallow rule was not found. 44 */ 45 protected AbstractImportControl(AbstractImportControl parent, 46 MismatchStrategy strategyOnMismatch) { 47 this.parent = parent; 48 this.strategyOnMismatch = strategyOnMismatch; 49 } 50 51 /** 52 * Search down the tree to locate the finest match for a supplied package. 53 * 54 * @param forPkg the package to search for. 55 * @param forFileName the file name to search for. 56 * @return the finest match, or null if no match at all. 57 */ 58 public abstract AbstractImportControl locateFinest(String forPkg, String forFileName); 59 60 /** 61 * Check for equality of this with pkg. 62 * 63 * @param pkg the package to compare with. 64 * @param fileName the file name to compare with. 65 * @return if it matches. 66 */ 67 protected abstract boolean matchesExactly(String pkg, String fileName); 68 69 /** 70 * Adds an {@link AbstractImportRule} to the node. 71 * 72 * @param rule the rule to be added. 73 */ 74 protected void addImportRule(AbstractImportRule rule) { 75 rules.addLast(rule); 76 } 77 78 /** 79 * Returns whether a package or class is allowed to be imported. 80 * The algorithm checks with the current node for a result, and if none is 81 * found then calls its parent looking for a match. This will recurse 82 * looking for match. If there is no clear result then 83 * {@link AccessResult#UNKNOWN} is returned. 84 * 85 * @param inPkg the package doing the import. 86 * @param inFileName the file name doing the import. 87 * @param forImport the import to check on. 88 * @return an {@link AccessResult}. 89 */ 90 public AccessResult checkAccess(String inPkg, String inFileName, String forImport) { 91 final AccessResult result; 92 final AccessResult returnValue = localCheckAccess(inPkg, inFileName, forImport); 93 if (returnValue != AccessResult.UNKNOWN) { 94 result = returnValue; 95 } 96 else if (parent == null) { 97 if (strategyOnMismatch == MismatchStrategy.ALLOWED) { 98 result = AccessResult.ALLOWED; 99 } 100 else { 101 result = AccessResult.DISALLOWED; 102 } 103 } 104 else { 105 if (strategyOnMismatch == MismatchStrategy.ALLOWED) { 106 result = AccessResult.ALLOWED; 107 } 108 else if (strategyOnMismatch == MismatchStrategy.DISALLOWED) { 109 result = AccessResult.DISALLOWED; 110 } 111 else { 112 result = parent.checkAccess(inPkg, inFileName, forImport); 113 } 114 } 115 return result; 116 } 117 118 /** 119 * Checks whether any of the rules for this node control access to 120 * a specified package or file. 121 * 122 * @param inPkg the package doing the import. 123 * @param inFileName the file name doing the import. 124 * @param forImport the import to check on. 125 * @return an {@link AccessResult}. 126 */ 127 private AccessResult localCheckAccess(String inPkg, String inFileName, String forImport) { 128 AccessResult localCheckAccessResult = AccessResult.UNKNOWN; 129 for (AbstractImportRule importRule : rules) { 130 // Check if an import rule is only meant to be applied locally. 131 if (!importRule.isLocalOnly() || matchesExactly(inPkg, inFileName)) { 132 final AccessResult result = importRule.verifyImport(forImport); 133 if (result != AccessResult.UNKNOWN) { 134 localCheckAccessResult = result; 135 break; 136 } 137 } 138 } 139 return localCheckAccessResult; 140 } 141 142 }