1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 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 }