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