AbstractImportControl.java
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2021 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.checks.imports;
import java.util.Deque;
import java.util.LinkedList;
/**
* Represents a tree of import rules for controlling whether packages or
* classes are allowed to be used. Each instance must have a single parent or
* be the root node.
*/
abstract class AbstractImportControl {
/** List of {@link AbstractImportRule} objects to check. */
private final Deque<AbstractImportRule> rules = new LinkedList<>();
/** The parent. Null indicates we are the root node. */
private final AbstractImportControl parent;
/** Strategy in a case if matching allow/disallow rule was not found. */
private final MismatchStrategy strategyOnMismatch;
/**
* Construct a child node.
*
* @param parent the parent node.
* @param strategyOnMismatch strategy in a case if matching allow/disallow rule was not found.
*/
protected AbstractImportControl(AbstractImportControl parent,
MismatchStrategy strategyOnMismatch) {
this.parent = parent;
this.strategyOnMismatch = strategyOnMismatch;
}
/**
* Search down the tree to locate the finest match for a supplied package.
*
* @param forPkg the package to search for.
* @param forFileName the file name to search for.
* @return the finest match, or null if no match at all.
*/
public abstract AbstractImportControl locateFinest(String forPkg, String forFileName);
/**
* Check for equality of this with pkg.
*
* @param pkg the package to compare with.
* @param fileName the file name to compare with.
* @return if it matches.
*/
protected abstract boolean matchesExactly(String pkg, String fileName);
/**
* Adds an {@link AbstractImportRule} to the node.
*
* @param rule the rule to be added.
*/
protected void addImportRule(AbstractImportRule rule) {
rules.addLast(rule);
}
/**
* Returns whether a package or class is allowed to be imported.
* The algorithm checks with the current node for a result, and if none is
* found then calls its parent looking for a match. This will recurse
* looking for match. If there is no clear result then
* {@link AccessResult#UNKNOWN} is returned.
*
* @param inPkg the package doing the import.
* @param inFileName the file name doing the import.
* @param forImport the import to check on.
* @return an {@link AccessResult}.
*/
public AccessResult checkAccess(String inPkg, String inFileName, String forImport) {
final AccessResult result;
final AccessResult returnValue = localCheckAccess(inPkg, inFileName, forImport);
if (returnValue != AccessResult.UNKNOWN) {
result = returnValue;
}
else if (parent == null) {
if (strategyOnMismatch == MismatchStrategy.ALLOWED) {
result = AccessResult.ALLOWED;
}
else {
result = AccessResult.DISALLOWED;
}
}
else {
if (strategyOnMismatch == MismatchStrategy.ALLOWED) {
result = AccessResult.ALLOWED;
}
else if (strategyOnMismatch == MismatchStrategy.DISALLOWED) {
result = AccessResult.DISALLOWED;
}
else {
result = parent.checkAccess(inPkg, inFileName, forImport);
}
}
return result;
}
/**
* Checks whether any of the rules for this node control access to
* a specified package or file.
*
* @param inPkg the package doing the import.
* @param inFileName the file name doing the import.
* @param forImport the import to check on.
* @return an {@link AccessResult}.
*/
private AccessResult localCheckAccess(String inPkg, String inFileName, String forImport) {
AccessResult localCheckAccessResult = AccessResult.UNKNOWN;
for (AbstractImportRule importRule : rules) {
// Check if an import rule is only meant to be applied locally.
if (!importRule.isLocalOnly() || matchesExactly(inPkg, inFileName)) {
final AccessResult result = importRule.verifyImport(forImport);
if (result != AccessResult.UNKNOWN) {
localCheckAccessResult = result;
break;
}
}
}
return localCheckAccessResult;
}
}