001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2020 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.net.URI;
023import java.util.Collections;
024import java.util.Set;
025import java.util.regex.Pattern;
026
027import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
028import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
029import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
030import com.puppycrawl.tools.checkstyle.api.DetailAST;
031import com.puppycrawl.tools.checkstyle.api.ExternalResourceHolder;
032import com.puppycrawl.tools.checkstyle.api.FullIdent;
033import com.puppycrawl.tools.checkstyle.api.TokenTypes;
034
035/**
036 * <p>
037 * Controls what can be imported in each package and file. Useful for ensuring
038 * that application layering rules are not violated, especially on large projects.
039 * </p>
040 * <p>
041 * You can control imports based on the a package name or based on the file name.
042 * When controlling packages, all files and sub-packages in the declared package
043 * will be controlled by this check. To specify differences between a main package
044 * and a sub-package, you must define the sub-package inside the main package.
045 * When controlling file, only the file name is considered and only files processed by
046 * <a href="https://checkstyle.org/config.html#TreeWalker">TreeWalker</a>.
047 * The file's extension is ignored.
048 * </p>
049 * <p>
050 * Short description of the behaviour:
051 * </p>
052 * <ul>
053 * <li>
054 * Check starts checking from the longest matching subpackage (later 'current subpackage') or
055 * the first file name match described inside import control file to package defined in class file.
056 * <ul>
057 * <li>
058 * The longest matching subpackage is found by starting with the root package and
059 * examining if the any of the sub-packages or file definitions match the current
060 * class' package or file name.
061 * </li>
062 * <li>
063 * If a file name is matched first, that is considered the longest match and becomes
064 * the current file/subpackage.
065 * </li>
066 * <li>
067 * If another subpackage is matched, then it's subpackages and file names are examined
068 * for the next longest match and the process repeats recursively.
069 * </li>
070 * <li>
071 * If no subpackages or file names are matched, the current subpackage is then used.
072 * </li>
073 * </ul>
074 * </li>
075 * <li>
076 * Order of rules in the same subpackage/root are defined by the order of declaration
077 * in the XML file, which is from top (first) to bottom (last).
078 * </li>
079 * <li>
080 * If there is matching allow/disallow rule inside the current file/subpackage
081 * then the Check returns the first "allowed" or "disallowed" message.
082 * </li>
083 * <li>
084 * If there is no matching allow/disallow rule inside the current file/subpackage
085 * then it continues checking in the parent subpackage.
086 * </li>
087 * <li>
088 * If there is no matching allow/disallow rule in any of the files/subpackages,
089 * including the root level (import-control), then the import is disallowed by default.
090 * </li>
091 * </ul>
092 * <p>
093 * The DTD for a import control XML document is at
094 * <a href="https://checkstyle.org/dtds/import_control_1_4.dtd">
095 * https://checkstyle.org/dtds/import_control_1_4.dtd</a>.
096 * It contains documentation on each of the elements and attributes.
097 * </p>
098 * <p>
099 * The check validates a XML document when it loads the document. To validate against
100 * the above DTD, include the following document type declaration in your XML document:
101 * </p>
102 * <pre>
103 * &lt;!DOCTYPE import-control PUBLIC
104 *     "-//Checkstyle//DTD ImportControl Configuration 1.4//EN"
105 *     "https://checkstyle.org/dtds/import_control_1_4.dtd"&gt;
106 * </pre>
107 * <ul>
108 * <li>
109 * Property {@code file} - Specify the location of the file containing the
110 * import control configuration. It can be a regular file, URL or resource path.
111 * It will try loading the path as a URL first, then as a file, and finally as a resource.
112 * Type is {@code java.net.URI}.
113 * Default value is {@code null}.
114 * </li>
115 * <li>
116 * Property {@code path} - Specify the regular expression of file paths to which
117 * this check should apply. Files that don't match the pattern will not be checked.
118 * The pattern will be matched against the full absolute file path.
119 * Type is {@code java.util.regex.Pattern}.
120 * Default value is {@code ".*"}.
121 * </li>
122 * </ul>
123 * <p>
124 * To configure the check using an import control file called "config/import-control.xml",
125 * then have the following:
126 * </p>
127 * <pre>
128 * &lt;module name="ImportControl"&gt;
129 *   &lt;property name="file" value="config/import-control.xml"/&gt;
130 * &lt;/module&gt;
131 * </pre>
132 * <p>
133 * To configure the check to only check the "src/main" directory using an import
134 * control file called "config/import-control.xml", then have the following:
135 * </p>
136 * <pre>
137 * &lt;module name="ImportControl"&gt;
138 *   &lt;property name="file" value="config/import-control.xml"/&gt;
139 *   &lt;property name="path" value="^.*[\\/]src[\\/]main[\\/].*$"/&gt;
140 * &lt;/module&gt;
141 * </pre>
142 * <p>
143 * In the example below access to package {@code com.puppycrawl.tools.checkstyle.checks}
144 * and its subpackages is allowed from anywhere in {@code com.puppycrawl.tools.checkstyle}
145 * except from the {@code filters} subpackage where access to all {@code check}'s
146 * subpackages is disallowed. Two {@code java.lang.ref} classes are allowed by virtue
147 * of one regular expression instead of listing them in two separate allow rules
148 * (as it is done with the {@code Files} and {@code ClassPath} classes).
149 * </p>
150 * <pre>
151 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle"&gt;
152 *   &lt;disallow pkg="sun"/&gt;
153 *   &lt;allow pkg="com.puppycrawl.tools.checkstyle.api"/&gt;
154 *   &lt;allow pkg="com.puppycrawl.tools.checkstyle.checks"/&gt;
155 *   &lt;allow class="com.google.common.io.Files"/&gt;
156 *   &lt;allow class="com.google.common.reflect.ClassPath"/&gt;
157 *   &lt;subpackage name="filters"&gt;
158 *     &lt;allow class="java\.lang\.ref\.(Weak|Soft)Reference"
159 *       regex="true"/&gt;
160 *     &lt;disallow pkg="com\.puppycrawl\.tools\.checkstyle\.checks\.[^.]+"
161 *       regex="true"/&gt;
162 *     &lt;disallow pkg="com.puppycrawl.tools.checkstyle.ant"/&gt;
163 *     &lt;disallow pkg="com.puppycrawl.tools.checkstyle.gui"/&gt;
164 *   &lt;/subpackage&gt;
165 *   &lt;subpackage name="dao"&gt;
166 *     &lt;disallow pkg="javax.swing" exact-match="true"/&gt;
167 *   &lt;/subpackage&gt;
168 * &lt;/import-control&gt;
169 * </pre>
170 * <p>
171 * In the next example regular expressions are used to enforce a layering rule:
172 * In all {@code dao} packages it is not allowed to access UI layer code ({@code ui},
173 * {@code awt}, and {@code swing}). On the other hand it is not allowed to directly
174 * access {@code dao} and {@code service} layer from {@code ui} packages.
175 * The root package is also a regular expression that is used to handle old and
176 * new domain name with the same rules.
177 * </p>
178 * <pre>
179 * &lt;import-control pkg="(de.olddomain|de.newdomain)\..*" regex="true"&gt;
180 *   &lt;subpackage pkg="[^.]+\.dao" regex="true"&gt;
181 *     &lt;disallow pkg=".*\.ui" regex="true"/&gt;
182 *     &lt;disallow pkg=".*\.(awt|swing).\.*" regex="true"/&gt;
183 *   &lt;/subpackage&gt;
184 *   &lt;subpackage pkg="[^.]+\.ui" regex="true"&gt;
185 *     &lt;disallow pkg=".*\.(dao|service)" regex="true"/&gt;
186 *   &lt;/subpackage&gt;
187 * &lt;/import-control&gt;
188 * </pre>
189 * <p>
190 * In the next examples usage of {@code strategyOnMismatch} property is shown.
191 * This property defines strategy in a case when no matching allow/disallow rule was found.
192 * Property {@code strategyOnMismatch} is attribute of {@code import-control} and
193 * {@code subpackage} tags. Property can have following values for {@code import-control} tag:
194 * </p>
195 * <ul>
196 * <li>
197 * disallowed (default value) - if there is no matching allow/disallow rule in any of
198 * the subpackages, including the root level (import-control), then the import is disallowed.
199 * </li>
200 * <li>
201 * allowed - if there is no matching allow/disallow rule in any of the subpackages,
202 * including the root level, then the import is allowed.
203 * </li>
204 * </ul>
205 * <p>
206 * And following values for {@code subpackage} tags:
207 * </p>
208 * <ul>
209 * <li>
210 * delegateToParent (default value) - if there is no matching allow/disallow rule
211 * inside the current subpackage, then it continues checking in the parent subpackage.
212 * </li>
213 * <li>
214 * allowed - if there is no matching allow/disallow rule inside the current subpackage,
215 * then the import is allowed.
216 * </li>
217 * <li>
218 * disallowed - if there is no matching allow/disallow rule inside the current subpackage,
219 * then the import is disallowed.
220 * </li>
221 * </ul>
222 * <p>
223 * The following example demonstrates usage of {@code strategyOnMismatch}
224 * property for {@code import-control} tag. Here all imports are allowed except
225 * {@code java.awt.Image} and {@code java.io.File} classes.
226 * </p>
227 * <pre>
228 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle.checks"
229 *   strategyOnMismatch="allowed"&gt;
230 *   &lt;disallow class="java.awt.Image"/&gt;
231 *   &lt;disallow class="java.io.File"/&gt;
232 * &lt;/import-control&gt;
233 * </pre>
234 * <p>
235 * In the example below, any import is disallowed inside
236 * {@code com.puppycrawl.tools.checkstyle.checks.imports} package except imports
237 * from package {@code javax.swing} and class {@code java.io.File}.
238 * However, any import is allowed in the classes outside of
239 * {@code com.puppycrawl.tools.checkstyle.checks.imports} package.
240 * </p>
241 * <pre>
242 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle.checks"
243 *   strategyOnMismatch="allowed"&gt;
244 *   &lt;subpackage name="imports" strategyOnMismatch="disallowed"&gt;
245 *     &lt;allow pkg="javax.swing"/&gt;
246 *     &lt;allow class="java.io.File"/&gt;
247 *   &lt;/subpackage&gt;
248 * &lt;/import-control&gt;
249 * </pre>
250 * <p>
251 * When {@code strategyOnMismatch} has {@code allowed} or {@code disallowed}
252 * value for {@code subpackage} tag, it makes {@code subpackage} isolated from
253 * parent rules. In the next example, if no matching rule was found inside
254 * {@code com.puppycrawl.tools.checkstyle.checks.filters} then it continues
255 * checking in the parent subpackage, while for
256 * {@code com.puppycrawl.tools.checkstyle.checks.imports} import will be allowed by default.
257 * </p>
258 * <pre>
259 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle.checks"&gt;
260 *   &lt;allow class="java\.awt\.Image" regex="true"/&gt;
261 *   &lt;allow class="java\..*\.File" local-only="true" regex="true"/&gt;
262 *   &lt;subpackage name="imports" strategyOnMismatch="allowed"&gt;
263 *     &lt;allow pkg="javax\.swing" regex="true"/&gt;
264 *     &lt;allow pkg="java\.io" exact-match="true"
265 *       local-only="true" regex="true"/&gt;
266 *   &lt;/subpackage&gt;
267 *   &lt;subpackage name="filters"&gt;
268 *     &lt;allow class="javax.util.Date"/&gt;
269 *   &lt;/subpackage&gt;
270 * &lt;/import-control&gt;
271 * </pre>
272 * <p>
273 * In the example below, only file names that end with "Panel", "View", or "Dialog"
274 * in the package {@code gui} are disallowed to have imports from {@code com.mycompany.dao}
275 * and any {@code jdbc} package. In addition, only the file name named "PresentationModel"
276 * in the package {@code gui} are disallowed to have imports that match {@code javax.swing.J*}.
277 * All other imports in the package are allowed.
278 * </p>
279 * <pre>
280 * &lt;import-control pkg="com.mycompany.billing"&gt;
281 *   &lt;subpackage name="gui" strategyOnMismatch="allowed"&gt;
282 *     &lt;file name=".*(Panel|View|Dialog)" regex="true"&gt;
283 *       &lt;disallow pkg="com.mycompany.dao"/&gt;
284 *       &lt;disallow pkg=".*\.jdbc" regex="true"/&gt;
285 *     &lt;/file&gt;
286 *     &lt;file name="PresentationModel"&gt;
287 *       &lt;disallow pkg="javax\.swing\.J.*" regex="true"/&gt;
288 *     &lt;/file&gt;
289 *   &lt;/subpackage&gt;
290 * &lt;/import-control&gt;
291 * </pre>
292 * <p>
293 * For a real-life import control file look at the file called
294 * <a href="https://github.com/checkstyle/checkstyle/blob/master/config/import-control.xml">
295 * import-control.xml</a> which is part of the Checkstyle distribution.
296 * </p>
297 * <p id="blacklist-example">Example of blacklist mode</p>
298 * <p>
299 * To have a <b>blacklist mode</b>, it is required to have disallows inside
300 * subpackage and to have allow rule inside parent of the current subpackage
301 * to catch classes and packages those are not in the blacklist.
302 * </p>
303 * <p>
304 * In the example below any import from {@code java.util}({@code java.util.List},
305 * {@code java.util.Date}) package is allowed except {@code java.util.Map}
306 * inside subpackage {@code com.puppycrawl.tools.checkstyle.filters}.
307 * </p>
308 * <pre>
309 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle"&gt;
310 *   &lt;allow pkg="java.util"/&gt;
311 *   &lt;subpackage name="filters" &gt;
312 *     &lt;disallow class="java.util.Map"/&gt;
313 *   &lt;/subpackage&gt;
314 * &lt;/import-control&gt;
315 * </pre>
316 * <p>
317 * In the next example imports {@code java.util.stream.Stream} and
318 * {@code java.util.stream.Collectors} are disallowed inside
319 * {@code com.puppycrawl.tools.checkstyle.checks.imports} package, but because of
320 * {@code &lt;allow pkg="java.util.stream"/&gt;} every import from
321 * {@code java.util.stream} is allowed except described ones.
322 * </p>
323 * <pre>
324 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle.checks"&gt;
325 *   &lt;allow pkg="java.util.stream"/&gt;
326 *   &lt;subpackage name="imports"&gt;
327 *     &lt;disallow class="java.util.stream.Stream"/&gt;
328 *     &lt;disallow class="java.util.stream.Collectors"/&gt;
329 *   &lt;/subpackage&gt;
330 * &lt;/import-control&gt;
331 * </pre>
332 * <pre>
333 * package com.puppycrawl.tools.checkstyle.checks.imports;
334 *
335 * import java.util.stream.Stream;     // violation here
336 * import java.util.stream.Collectors; // violation here
337 * import java.util.stream.IntStream;
338 * </pre>
339 * <p>
340 * In the following example, all imports are allowed except the classes
341 * {@code java.util.Date}, {@code java.util.List} and package {@code sun}.
342 * </p>
343 * <pre>
344 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle.checks"&gt;
345 *   &lt;allow pkg=".*" regex="true"/&gt;
346 *   &lt;subpackage name="imports"&gt;
347 *     &lt;disallow class="java.util.Date"/&gt;
348 *     &lt;disallow class="java.util.List"/&gt;
349 *     &lt;disallow pkg="sun"/&gt;
350 *   &lt;/subpackage&gt;
351 * &lt;/import-control&gt;
352 * </pre>
353 * <p>
354 * In the following example, all imports of the {@code java.util} package are
355 * allowed except the {@code java.util.Date} class.
356 * </p>
357 * <pre>
358 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle.checks"&gt;
359 *   &lt;disallow class="java.util.Date"/&gt;
360 *
361 *   &lt;allow pkg="java.util"/&gt;
362 * &lt;/import-control&gt;
363 * </pre>
364 * <p id="regex-notes">Notes on regular expressions</p>
365 * <p>
366 * Regular expressions in import rules have to match either Java packages or classes.
367 * The language rules for packages and class names can be described by the following
368 * complicated regular expression that takes into account that Java names may contain
369 * any unicode letter, numbers, underscores, and dollar signs (see section 3.8 in the
370 * <a href="https://docs.oracle.com/javase/specs/">Java specs</a>):
371 * </p>
372 * <ul>
373 * <li>
374 * {@code [\p{Letter}_$][\p{Letter}\p{Number}_$]*} or short {@code [\p{L}_$][\p{L}\p{N}_$]*}
375 * for a class name or package component.
376 * </li>
377 * <li>
378 * {@code ([\p{L}_$][\p{L}\p{N}_$]*\.)*[\p{L}_$][\p{L}\p{N}_$]*} for a fully qualified name.
379 * </li>
380 * </ul>
381 * <p>
382 * But it is not necessary to use these complicated expressions since no validation is required.
383 * Differentiating between package separator '.' and others is sufficient.
384 * Unfortunately '.' has a special meaning in regular expressions so one has to write {@code \.}
385 * to match an actual dot.
386 * </p>
387 * <ul>
388 * <li>
389 * Use {@code [^.]+}(one or more "not a dot" characters) for a class name or package component.
390 * </li>
391 * <li>
392 * Use {@code com\.google\.common\.[^.]+} to match any subpackage of {@code com.google.common}.
393 * </li>
394 * <li>
395 * When matching concrete packages like {@code com.google.common} omitting the backslash before
396 * the dots may improve readability and may be just exact enough: {@code com.google.common\.[^.]+}
397 * matches not only subpackages of {@code com.google.common} but e.g. also of
398 * {@code com.googleecommon} but you may not care for that.
399 * </li>
400 * <li>
401 * Do not use {@code .*} unless you really do not care for what is matched.
402 * Often you want to match only a certain package level instead.
403 * </li>
404 * </ul><p id="static-import-notes">Notes on static imports</p>
405 * <p>
406 * Static members (including methods, constants and static inner classes)
407 * have to be explicitly allowed when they are imported, they are not automatically
408 * allowed along with their enclosing class.
409 * </p>
410 * <p>
411 * For example, to allow importing both {@code java.util.Map} and {@code java.util.Map.Entry}
412 * use the following configuration:
413 * </p>
414 * <pre>
415 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle"&gt;
416 *   &lt;allow class="java.util.Map"/&gt;
417 *   &lt;allow class="java.util.Map.Entry"/&gt;
418 * &lt;/import-control&gt;
419 * </pre>
420 * <p>
421 * It is also possible to use a regex with a wildcard:
422 * </p>
423 * <pre>
424 * &lt;import-control pkg="com.puppycrawl.tools.checkstyle"&gt;
425 *   &lt;allow class="java.util.Map"/&gt;
426 *   &lt;allow class="java.util.Map.*" regex="true" /&gt;
427 * &lt;/import-control&gt;
428 * </pre>
429 * <p>
430 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
431 * </p>
432 * <p>
433 * Violation Message Keys:
434 * </p>
435 * <ul>
436 * <li>
437 * {@code import.control.disallowed}
438 * </li>
439 * <li>
440 * {@code import.control.missing.file}
441 * </li>
442 * <li>
443 * {@code import.control.unknown.pkg}
444 * </li>
445 * </ul>
446 *
447 * @since 4.0
448 */
449@FileStatefulCheck
450public class ImportControlCheck extends AbstractCheck implements ExternalResourceHolder {
451
452    /**
453     * A key is pointing to the warning message text in "messages.properties"
454     * file.
455     */
456    public static final String MSG_MISSING_FILE = "import.control.missing.file";
457
458    /**
459     * A key is pointing to the warning message text in "messages.properties"
460     * file.
461     */
462    public static final String MSG_UNKNOWN_PKG = "import.control.unknown.pkg";
463
464    /**
465     * A key is pointing to the warning message text in "messages.properties"
466     * file.
467     */
468    public static final String MSG_DISALLOWED = "import.control.disallowed";
469
470    /**
471     * A part of message for exception.
472     */
473    private static final String UNABLE_TO_LOAD = "Unable to load ";
474
475    /**
476     * Specify the location of the file containing the import control configuration.
477     * It can be a regular file, URL or resource path. It will try loading the path
478     * as a URL first, then as a file, and finally as a resource.
479     */
480    private URI file;
481
482    /**
483     * Specify the regular expression of file paths to which this check should apply.
484     * Files that don't match the pattern will not be checked. The pattern will
485     * be matched against the full absolute file path.
486     */
487    private Pattern path = Pattern.compile(".*");
488    /** Whether to process the current file. */
489    private boolean processCurrentFile;
490
491    /** The root package controller. */
492    private PkgImportControl root;
493    /** The package doing the import. */
494    private String packageName;
495    /** The file name doing the import. */
496    private String fileName;
497
498    /**
499     * The package controller for the current file. Used for performance
500     * optimisation.
501     */
502    private AbstractImportControl currentImportControl;
503
504    @Override
505    public int[] getDefaultTokens() {
506        return getRequiredTokens();
507    }
508
509    @Override
510    public int[] getAcceptableTokens() {
511        return getRequiredTokens();
512    }
513
514    @Override
515    public int[] getRequiredTokens() {
516        return new int[] {TokenTypes.PACKAGE_DEF, TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT, };
517    }
518
519    @Override
520    public void beginTree(DetailAST rootAST) {
521        currentImportControl = null;
522        processCurrentFile = path.matcher(getFileContents().getFileName()).find();
523        fileName = getFileContents().getText().getFile().getName();
524
525        final int period = fileName.lastIndexOf('.');
526
527        if (period != -1) {
528            fileName = fileName.substring(0, period);
529        }
530    }
531
532    @Override
533    public void visitToken(DetailAST ast) {
534        if (processCurrentFile) {
535            if (ast.getType() == TokenTypes.PACKAGE_DEF) {
536                if (root == null) {
537                    log(ast, MSG_MISSING_FILE);
538                }
539                else {
540                    packageName = getPackageText(ast);
541                    currentImportControl = root.locateFinest(packageName, fileName);
542                    if (currentImportControl == null) {
543                        log(ast, MSG_UNKNOWN_PKG);
544                    }
545                }
546            }
547            else if (currentImportControl != null) {
548                final String importText = getImportText(ast);
549                final AccessResult access = currentImportControl.checkAccess(packageName, fileName,
550                        importText);
551                if (access != AccessResult.ALLOWED) {
552                    log(ast, MSG_DISALLOWED, importText);
553                }
554            }
555        }
556    }
557
558    @Override
559    public Set<String> getExternalResourceLocations() {
560        return Collections.singleton(file.toString());
561    }
562
563    /**
564     * Returns package text.
565     *
566     * @param ast PACKAGE_DEF ast node
567     * @return String that represents full package name
568     */
569    private static String getPackageText(DetailAST ast) {
570        final DetailAST nameAST = ast.getLastChild().getPreviousSibling();
571        return FullIdent.createFullIdent(nameAST).getText();
572    }
573
574    /**
575     * Returns import text.
576     *
577     * @param ast ast node that represents import
578     * @return String that represents importing class
579     */
580    private static String getImportText(DetailAST ast) {
581        final FullIdent imp;
582        if (ast.getType() == TokenTypes.IMPORT) {
583            imp = FullIdent.createFullIdentBelow(ast);
584        }
585        else {
586            // know it is a static import
587            imp = FullIdent.createFullIdent(ast
588                    .getFirstChild().getNextSibling());
589        }
590        return imp.getText();
591    }
592
593    /**
594     * Setter to specify the location of the file containing the import control configuration.
595     * It can be a regular file, URL or resource path. It will try loading the path
596     * as a URL first, then as a file, and finally as a resource.
597     *
598     * @param uri the uri of the file to load.
599     * @throws IllegalArgumentException on error loading the file.
600     */
601    public void setFile(URI uri) {
602        // Handle empty param
603        if (uri != null) {
604            try {
605                root = ImportControlLoader.load(uri);
606                file = uri;
607            }
608            catch (CheckstyleException ex) {
609                throw new IllegalArgumentException(UNABLE_TO_LOAD + uri, ex);
610            }
611        }
612    }
613
614    /**
615     * Setter to specify the regular expression of file paths to which this check should apply.
616     * Files that don't match the pattern will not be checked. The pattern will be matched
617     * against the full absolute file path.
618     *
619     * @param pattern the file path regex this check should apply to.
620     */
621    public void setPath(Pattern pattern) {
622        path = pattern;
623    }
624
625}