001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2019 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.util.Locale;
023import java.util.regex.Matcher;
024import java.util.regex.Pattern;
025
026import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
027import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.api.FullIdent;
030import com.puppycrawl.tools.checkstyle.api.TokenTypes;
031import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
032
033/**
034 * <p>
035 * Checks the ordering/grouping of imports. Features are:
036 * </p>
037 * <ul>
038 * <li>
039 * groups type/static imports: ensures that groups of imports come in a specific order
040 * (e.g., java. comes first, javax. comes second, then everything else)
041 * </li>
042 * <li>
043 * adds a separation between type import groups : ensures that a blank line sit between each group
044 * </li>
045 * <li>
046 * type/static import groups aren't separated internally: ensures that each group aren't separated
047 * internally by blank line or comment
048 * </li>
049 * <li>
050 * sorts type/static imports inside each group: ensures that imports within each group are in
051 * lexicographic order
052 * </li>
053 * <li>
054 * sorts according to case: ensures that the comparison between imports is case sensitive, in
055 * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>
056 * </li>
057 * <li>
058 * arrange static imports: ensures the relative order between type imports and static imports
059 * (see <a href="property_types.html#importOrder">import orders</a>)
060 * </li>
061 * </ul>
062 * <ul>
063 * <li>
064 * Property {@code option} - specify policy on the relative order between type imports and static
065 * imports.
066 * Default value is {@code under}.
067 * </li>
068 * <li>
069 * Property {@code groups} - specify list of <b>type import</b> groups (every group identified
070 * either by a common prefix string, or by a regular expression enclosed in forward slashes
071 * (e.g.{@code /regexp/}). All type imports, which does not match any group, falls into an
072 * additional group, located at the end.
073 * Thus, the empty list of type groups (the default value) means one group for all type imports.
074 * Default value is {@code {}}.
075 * </li>
076 * <li>
077 * Property {@code ordered} - control whether type imports within each group should be
078 * sorted.
079 * It doesn't affect sorting for static imports.
080 * Default value is true.
081 * </li>
082 * <li>
083 * Property {@code separated} - control whether type import groups should be separated
084 * by, at least, one blank line or comment and aren't separated internally.
085 * It doesn't affect separations for static imports.
086 * Default value is false.
087 * </li>
088 * <li>
089 * Property {@code separatedStaticGroups} - control whether static import groups should
090 * be separated by, at least, one blank line or comment and aren't separated internally.
091 * This property has effect only when the property {@code option} is is set to {@code top}
092 * or {@code bottom}.
093 * Default value is false.
094 * </li>
095 * <li>
096 * Property {@code caseSensitive} - control whether string comparison should be case
097 * sensitive or not. Case sensitive sorting is in
098 * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>.
099 * It affects both type imports and static imports.
100 * Default value is true.
101 * </li>
102 * <li>
103 * Property {@code staticGroups} - specify list of <b>static</b> import groups (every group
104 * identified either by a common prefix string, or by a regular expression enclosed in forward
105 * slashes (e.g.{@code /regexp/}). All static imports, which does not match any group, falls into an
106 * additional group, located at the end. Thus, the empty list of static groups (the default value)
107 * means one group for all static imports. This property has effect only when the property
108 * {@code option} is set to {@code top} or {@code bottom}.
109 * Default value is {@code {}}.
110 * </li>
111 * <li>
112 * Property {@code sortStaticImportsAlphabetically} - control whether
113 * <b>static imports</b> located at <b>top</b> or <b>bottom</b> are sorted within the group.
114 * Default value is false.
115 * </li>
116 * <li>
117 * Property {@code useContainerOrderingForStatic} - control whether to use container
118 * ordering (Eclipse IDE term) for static imports or not.
119 * Default value is false.
120 * </li>
121 * <li>
122 * Property {@code tokens} - tokens to check
123 * Default value is:
124 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_IMPORT">
125 * STATIC_IMPORT</a>.
126 * </li>
127 * </ul>
128 * <p>
129 * To configure the check so that it matches default Eclipse formatter configuration
130 * (tested on Kepler and Luna releases):
131 * </p>
132 * <ul>
133 * <li>
134 * group of static imports is on the top
135 * </li>
136 * <li>
137 * groups of type imports: "java" and "javax" packages first, then "org" and then all other imports
138 * </li>
139 * <li>
140 * imports will be sorted in the groups
141 * </li>
142 * <li>
143 * groups are separated by, at least, one blank line and aren't separated internally
144 * </li>
145 * </ul>
146 * <p>
147 * Notes:
148 * </p>
149 * <ul>
150 * <li>
151 * "com" package is not mentioned on configuration, because it is ignored by Eclipse Kepler and Luna
152 * (looks like Eclipse defect)
153 * </li>
154 * <li>
155 * configuration below doesn't work in all 100% cases due to inconsistent behavior prior to
156 * Mars release, but covers most scenarios
157 * </li>
158 * </ul>
159 * <pre>
160 * &lt;module name=&quot;ImportOrder&quot;&gt;
161 *   &lt;property name=&quot;groups&quot; value=&quot;/^java\./,javax,org&quot;/&gt;
162 *   &lt;property name=&quot;ordered&quot; value=&quot;true&quot;/&gt;
163 *   &lt;property name=&quot;separated&quot; value=&quot;true&quot;/&gt;
164 *   &lt;property name=&quot;option&quot; value=&quot;above&quot;/&gt;
165 *   &lt;property name=&quot;sortStaticImportsAlphabetically&quot; value=&quot;true&quot;/&gt;
166 * &lt;/module&gt;
167 * </pre>
168 * <p>
169 * To configure the check so that it matches default Eclipse formatter configuration
170 * (tested on Mars release):
171 * </p>
172 * <ul>
173 * <li>
174 * group of static imports is on the top
175 * </li>
176 * <li>
177 * groups of type imports: "java" and "javax" packages first, then "org" and "com",
178 * then all other imports as one group
179 * </li>
180 * <li>
181 * imports will be sorted in the groups
182 * </li>
183 * <li>
184 * groups are separated by, at least, one blank line and aren't separated internally
185 * </li>
186 * </ul>
187 * <pre>
188 * &lt;module name=&quot;ImportOrder&quot;&gt;
189 *   &lt;property name=&quot;groups&quot; value=&quot;/^java\./,javax,org,com&quot;/&gt;
190 *   &lt;property name=&quot;ordered&quot; value=&quot;true&quot;/&gt;
191 *   &lt;property name=&quot;separated&quot; value=&quot;true&quot;/&gt;
192 *   &lt;property name=&quot;option&quot; value=&quot;above&quot;/&gt;
193 *   &lt;property name=&quot;sortStaticImportsAlphabetically&quot; value=&quot;true&quot;/&gt;
194 * &lt;/module&gt;
195 * </pre>
196 * <p>
197 * To configure the check so that it matches default IntelliJ IDEA formatter configuration
198 * (tested on v2018.2):
199 * </p>
200 * <ul>
201 * <li>
202 * group of static imports is on the bottom
203 * </li>
204 * <li>
205 * groups of type imports: all imports except of "javax" and "java", then "javax" and "java"
206 * </li>
207 * <li>
208 * imports will be sorted in the groups
209 * </li>
210 * <li>
211 * groups are separated by, at least, one blank line and aren't separated internally
212 * </li>
213 * </ul>
214 * <p>
215 * Note: a <a href="config_filters.html#SuppressionXpathSingleFilter">
216 * suppression xpath single filter</a> is needed because
217 * IDEA has no blank line between "javax" and "java".
218 * ImportOrder has a limitation by design to enforce an empty line between groups ("java", "javax").
219 * There is no flexibility to enforce empty lines between some groups and no empty lines between
220 * other groups.
221 * </p>
222 * <p>
223 * Note: "separated" option is disabled because IDEA default has blank line between "java" and
224 * static imports, and no blank line between "javax" and "java".
225 * </p>
226 * <pre>
227 * &lt;module name=&quot;ImportOrder&quot;&gt;
228 *   &lt;property name=&quot;groups&quot; value=&quot;*,javax,java&quot;/&gt;
229 *   &lt;property name=&quot;ordered&quot; value=&quot;true&quot;/&gt;
230 *   &lt;property name=&quot;separated&quot; value=&quot;false&quot;/&gt;
231 *   &lt;property name=&quot;option&quot; value=&quot;bottom&quot;/&gt;
232 *   &lt;property name=&quot;sortStaticImportsAlphabetically&quot; value=&quot;true&quot;/&gt;
233 * &lt;/module&gt;
234 * &lt;module name="SuppressionXpathSingleFilter"&gt;
235 *   &lt;property name="checks" value="ImportOrder"/&gt;
236 *   &lt;property name="message" value="^'java\..*'.*"/&gt;
237 * &lt;/module&gt;
238 * </pre>
239 * <p>
240 * To configure the check so that it matches default NetBeans formatter configuration
241 * (tested on v8):
242 * </p>
243 * <ul>
244 * <li>
245 * groups of type imports are not defined, all imports will be sorted as a one group
246 * </li>
247 * <li>
248 * static imports are not separated, they will be sorted along with other imports
249 * </li>
250 * </ul>
251 * <pre>
252 * &lt;module name=&quot;ImportOrder&quot;&gt;
253 *   &lt;property name=&quot;option&quot; value=&quot;inflow&quot;/&gt;
254 * &lt;/module&gt;
255 * </pre>
256 * <p>
257 * Group descriptions enclosed in slashes are interpreted as regular expressions.
258 * If multiple groups match, the one matching a longer substring of the imported name
259 * will take precedence, with ties broken first in favor of earlier matches and finally
260 * in favor of the first matching group.
261 * </p>
262 * <p>
263 * There is always a wildcard group to which everything not in a named group belongs.
264 * If an import does not match a named group, the group belongs to this wildcard group.
265 * The wildcard group position can be specified using the {@code *} character.
266 * </p>
267 * <p>
268 * Check also has on option making it more flexible: <b>sortStaticImportsAlphabetically</b>
269 * - sets whether static imports grouped by <b>top</b> or <b>bottom</b> option should be sorted
270 * alphabetically or not, default value is <b>false</b>. It is applied to static imports grouped
271 * with <b>top</b> or <b>bottom</b> options. This option is helping in reconciling of this
272 * Check and other tools like Eclipse's Organize Imports feature.
273 * </p>
274 * <p>
275 * To configure the Check allows static imports grouped to the <b>top</b> being sorted
276 * alphabetically:
277 * </p>
278 * <pre>
279 * &lt;module name=&quot;ImportOrder&quot;&gt;
280 *   &lt;property name=&quot;sortStaticImportsAlphabetically&quot; value=&quot;true&quot;/&gt;
281 *   &lt;property name=&quot;option&quot; value=&quot;top&quot;/&gt;
282 * &lt;/module&gt;
283 * </pre>
284 * <pre>
285 * import static java.lang.Math.PI;
286 * import static java.lang.Math.abs; // OK, alphabetical case sensitive ASCII order, 'P' &lt; 'a'
287 * import static org.abego.treelayout.Configuration.AlignmentInLevel; // OK, alphabetical order
288 *
289 * import org.abego.*;
290 *
291 * import java.util.Set; //  Wrong order for 'java.util.Set' import.
292 *
293 * public class SomeClass { ... }
294 * </pre>
295 * <p>
296 * To configure the Check with groups of static imports:
297 * </p>
298 * <pre>
299 * &lt;module name=&quot;ImportOrder&quot;&gt;
300 *   &lt;property name=&quot;staticGroups&quot; value=&quot;org,java&quot;/&gt;
301 *   &lt;property name=&quot;sortStaticImportsAlphabetically&quot; value=&quot;true&quot;/&gt;
302 * &lt;/module&gt;
303 * </pre>
304 * <pre>
305 * import static org.abego.treelayout.Configuration.AlignmentInLevel; // Group 1
306 * import static java.lang.Math.abs; // Group 2
307 * import static java.lang.String.format; // Group 2
308 * import static com.google.common.primitives.Doubles.BYTES; // Group "everything else"
309 *
310 * public class SomeClass { ... }
311 * </pre>
312 * <p>
313 * The following example shows the idea of 'useContainerOrderingForStatic' option that is
314 * useful for Eclipse IDE users to match ordering validation.
315 * This is how the import comparison works for static imports: we first compare
316 * the container of the static import, container is the type enclosing the static element
317 * being imported. When the result of the comparison is 0 (containers are equal),
318 * we compare the fully qualified import names.
319 * For e.g. this is what is considered to be container names for the given example:
320 *
321 * import static HttpConstants.COLON     =&gt; HttpConstants
322 * import static HttpHeaders.addHeader   =&gt; HttpHeaders
323 * import static HttpHeaders.setHeader   =&gt; HttpHeaders
324 * import static HttpHeaders.Names.DATE  =&gt; HttpHeaders.Names
325 *
326 * According to this logic, HttpHeaders.Names should come after HttpHeaders.
327 * </p> Example for {@code useContainerOrderingForStatic=true}
328 * <pre>
329 * &lt;module name=&quot;ImportOrder&quot;&gt;
330 *   &lt;property name=&quot;useContainerOrderingForStatic&quot; value=&quot;true&quot;/&gt;
331 *   &lt;property name=&quot;ordered&quot; value=&quot;true&quot;/&gt;
332 *   &lt;property name=&quot;option&quot; value=&quot;top&quot;/&gt;
333 *   &lt;property name=&quot;caseSensitive&quot; value=&quot;false&quot;/&gt;
334 *   &lt;property name=&quot;sortStaticImportsAlphabetically&quot; value=&quot;true&quot;/&gt;
335 * &lt;/module&gt;
336 * </pre>
337 * <pre>
338 * import static io.netty.handler.codec.http.HttpConstants.COLON;
339 * import static io.netty.handler.codec.http.HttpHeaders.addHeader;
340 * import static io.netty.handler.codec.http.HttpHeaders.setHeader;
341 * import static io.netty.handler.codec.http.HttpHeaders.Names.DATE;
342 *
343 * public class InputEclipseStaticImportsOrder { }
344 * </pre> Example for {@code useContainerOrderingForStatic=false}
345 * <pre>
346 * &lt;module name=&quot;ImportOrder&quot;&gt;
347 *   &lt;property name=&quot;useContainerOrderingForStatic&quot; value=&quot;false&quot;/&gt;
348 *   &lt;property name=&quot;ordered&quot; value=&quot;true&quot;/&gt;
349 *   &lt;property name=&quot;option&quot; value=&quot;top&quot;/&gt;
350 *   &lt;property name=&quot;caseSensitive&quot; value=&quot;false&quot;/&gt;
351 *   &lt;property name=&quot;sortStaticImportsAlphabetically&quot; value=&quot;true&quot;/&gt;
352 * &lt;/module&gt;
353 * </pre>
354 * <pre>
355 * import static io.netty.handler.codec.http.HttpConstants.COLON;
356 * import static io.netty.handler.codec.http.HttpHeaders.addHeader;
357 * import static io.netty.handler.codec.http.HttpHeaders.setHeader;
358 * import static io.netty.handler.codec.http.HttpHeaders.Names.DATE; // violation
359 *
360 * public class InputEclipseStaticImportsOrder { }
361 * </pre>
362 *
363 * @since 3.2
364 */
365@FileStatefulCheck
366public class ImportOrderCheck
367    extends AbstractCheck {
368
369    /**
370     * A key is pointing to the warning message text in "messages.properties"
371     * file.
372     */
373    public static final String MSG_SEPARATION = "import.separation";
374
375    /**
376     * A key is pointing to the warning message text in "messages.properties"
377     * file.
378     */
379    public static final String MSG_ORDERING = "import.ordering";
380
381    /**
382     * A key is pointing to the warning message text in "messages.properties"
383     * file.
384     */
385    public static final String MSG_SEPARATED_IN_GROUP = "import.groups.separated.internally";
386
387    /** The special wildcard that catches all remaining groups. */
388    private static final String WILDCARD_GROUP_NAME = "*";
389
390    /** Empty array of pattern type needed to initialize check. */
391    private static final Pattern[] EMPTY_PATTERN_ARRAY = new Pattern[0];
392
393    /**
394     * Specify list of <b>type import</b> groups (every group identified either by a common prefix
395     * string, or by a regular expression enclosed in forward slashes (e.g.{@code /regexp/}).
396     * All type imports, which does not match any group, falls into an additional group,
397     * located at the end. Thus, the empty list of type groups (the default value) means one group
398     * for all type imports.
399     */
400    private Pattern[] groups = EMPTY_PATTERN_ARRAY;
401
402    /**
403     * Specify list of <b>static</b> import groups (every group identified either by a common prefix
404     * string, or by a regular expression enclosed in forward slashes (e.g.{@code /regexp/}).
405     * All static imports, which does not match any group, falls into an additional group, located
406     * at the end. Thus, the empty list of static groups (the default value) means one group for all
407     * static imports. This property has effect only when the property {@code option} is set to
408     * {@code top} or {@code bottom}.
409     */
410    private Pattern[] staticGroups = EMPTY_PATTERN_ARRAY;
411
412    /**
413     * Control whether type import groups should be separated by, at least, one blank
414     * line or comment and aren't separated internally. It doesn't affect separations for static
415     * imports.
416     */
417    private boolean separated;
418
419    /**
420     * Control whether static import groups should be separated by, at least, one blank
421     * line or comment and aren't separated internally. This property has effect only when the
422     * property {@code option} is is set to {@code top} or {@code bottom}.
423     */
424    private boolean separatedStaticGroups;
425
426    /**
427     * Control whether type imports within each group should be sorted.
428     * It doesn't affect sorting for static imports.
429     */
430    private boolean ordered = true;
431
432    /**
433     * Control whether string comparison should be case sensitive or not. Case sensitive
434     * sorting is in <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>.
435     * It affects both type imports and static imports.
436     */
437    private boolean caseSensitive = true;
438
439    /** Last imported group. */
440    private int lastGroup;
441    /** Line number of last import. */
442    private int lastImportLine;
443    /** Name of last import. */
444    private String lastImport;
445    /** If last import was static. */
446    private boolean lastImportStatic;
447    /** Whether there was any imports. */
448    private boolean beforeFirstImport;
449    /** Whether static and type import groups should be split apart.
450     * When the {@code option} property is set to {@code INFLOW}, {@code BELOW} or {@code UNDER},
451     * both the type and static imports use the properties {@code groups} and {@code separated}.
452     * When the {@code option} property is set to {@code TOP} or {@code BOTTOM}, static imports
453     * uses the properties {@code staticGroups} and {@code separatedStaticGroups}.
454     **/
455    private boolean staticImportsApart;
456
457    /**
458     * Control whether <b>static imports</b> located at <b>top</b> or <b>bottom</b> are
459     * sorted within the group.
460     */
461    private boolean sortStaticImportsAlphabetically;
462
463    /**
464     * Control whether to use container ordering (Eclipse IDE term) for static imports
465     * or not.
466     */
467    private boolean useContainerOrderingForStatic;
468
469    /**
470     * Specify policy on the relative order between type imports and static imports.
471     */
472    private ImportOrderOption option = ImportOrderOption.UNDER;
473
474    /**
475     * Setter to specify policy on the relative order between type imports and static imports.
476     * @param optionStr string to decode option from
477     * @throws IllegalArgumentException if unable to decode
478     */
479    public void setOption(String optionStr) {
480        option = ImportOrderOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
481    }
482
483    /**
484     * Setter to specify list of <b>type import</b> groups (every group identified either by a
485     * common prefix string, or by a regular expression enclosed in forward slashes
486     * (e.g.{@code /regexp/}). All type imports, which does not match any group, falls into an
487     * additional group, located at the end. Thus, the empty list of type groups (the default value)
488     * means one group for all type imports.
489     *
490     * @param packageGroups a comma-separated list of package names/prefixes.
491     */
492    public void setGroups(String... packageGroups) {
493        groups = compilePatterns(packageGroups);
494    }
495
496    /**
497     * Setter to specify list of <b>static</b> import groups (every group identified either by a
498     * common prefix string, or by a regular expression enclosed in forward slashes
499     * (e.g.{@code /regexp/}). All static imports, which does not match any group, falls into an
500     * additional group, located at the end. Thus, the empty list of static groups (the default
501     * value) means one group for all static imports. This property has effect only when
502     * the property {@code option} is set to {@code top} or {@code bottom}.
503     *
504     * @param packageGroups a comma-separated list of package names/prefixes.
505     */
506    public void setStaticGroups(String... packageGroups) {
507        staticGroups = compilePatterns(packageGroups);
508    }
509
510    /**
511     * Setter to control whether type imports within each group should be sorted.
512     * It doesn't affect sorting for static imports.
513     *
514     * @param ordered
515     *            whether lexicographic ordering of imports within a group
516     *            required or not.
517     */
518    public void setOrdered(boolean ordered) {
519        this.ordered = ordered;
520    }
521
522    /**
523     * Setter to control whether type import groups should be separated by, at least,
524     * one blank line or comment and aren't separated internally.
525     * It doesn't affect separations for static imports.
526     *
527     * @param separated
528     *            whether groups should be separated by one blank line or comment.
529     */
530    public void setSeparated(boolean separated) {
531        this.separated = separated;
532    }
533
534    /**
535     * Setter to control whether static import groups should be separated by, at least,
536     * one blank line or comment and aren't separated internally.
537     * This property has effect only when the property
538     * {@code option} is is set to {@code top} or {@code bottom}.
539     *
540     * @param separatedStaticGroups
541     *            whether groups should be separated by one blank line or comment.
542     */
543    public void setSeparatedStaticGroups(boolean separatedStaticGroups) {
544        this.separatedStaticGroups = separatedStaticGroups;
545    }
546
547    /**
548     * Setter to control whether string comparison should be case sensitive or not.
549     * Case sensitive sorting is in
550     * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>.
551     * It affects both type imports and static imports.
552     * @param caseSensitive
553     *            whether string comparison should be case sensitive.
554     */
555    public void setCaseSensitive(boolean caseSensitive) {
556        this.caseSensitive = caseSensitive;
557    }
558
559    /**
560     * Setter to control whether <b>static imports</b> located at <b>top</b> or
561     * <b>bottom</b> are sorted within the group.
562     * @param sortAlphabetically true or false.
563     */
564    public void setSortStaticImportsAlphabetically(boolean sortAlphabetically) {
565        sortStaticImportsAlphabetically = sortAlphabetically;
566    }
567
568    /**
569     * Setter to control whether to use container ordering (Eclipse IDE term) for static
570     * imports or not.
571     * @param useContainerOrdering whether to use container ordering for static imports or not.
572     */
573    public void setUseContainerOrderingForStatic(boolean useContainerOrdering) {
574        useContainerOrderingForStatic = useContainerOrdering;
575    }
576
577    @Override
578    public int[] getDefaultTokens() {
579        return getAcceptableTokens();
580    }
581
582    @Override
583    public int[] getAcceptableTokens() {
584        return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT};
585    }
586
587    @Override
588    public int[] getRequiredTokens() {
589        return new int[] {TokenTypes.IMPORT};
590    }
591
592    @Override
593    public void beginTree(DetailAST rootAST) {
594        lastGroup = Integer.MIN_VALUE;
595        lastImportLine = Integer.MIN_VALUE;
596        lastImport = "";
597        lastImportStatic = false;
598        beforeFirstImport = true;
599        staticImportsApart =
600            option == ImportOrderOption.TOP || option == ImportOrderOption.BOTTOM;
601    }
602
603    // -@cs[CyclomaticComplexity] SWITCH was transformed into IF-ELSE.
604    @Override
605    public void visitToken(DetailAST ast) {
606        final int line = ast.getLineNo();
607        final FullIdent ident;
608        final boolean isStatic;
609
610        if (ast.getType() == TokenTypes.IMPORT) {
611            ident = FullIdent.createFullIdentBelow(ast);
612            isStatic = false;
613        }
614        else {
615            ident = FullIdent.createFullIdent(ast.getFirstChild()
616                    .getNextSibling());
617            isStatic = true;
618        }
619
620        // using set of IF instead of SWITCH to analyze Enum options to satisfy coverage.
621        // https://github.com/checkstyle/checkstyle/issues/1387
622        if (option == ImportOrderOption.TOP || option == ImportOrderOption.ABOVE) {
623            final boolean isStaticAndNotLastImport = isStatic && !lastImportStatic;
624            doVisitToken(ident, isStatic, isStaticAndNotLastImport, line);
625        }
626        else if (option == ImportOrderOption.BOTTOM || option == ImportOrderOption.UNDER) {
627            final boolean isLastImportAndNonStatic = lastImportStatic && !isStatic;
628            doVisitToken(ident, isStatic, isLastImportAndNonStatic, line);
629        }
630        else if (option == ImportOrderOption.INFLOW) {
631            // "previous" argument is useless here
632            doVisitToken(ident, isStatic, true, line);
633        }
634        else {
635            throw new IllegalStateException(
636                    "Unexpected option for static imports: " + option);
637        }
638
639        lastImportLine = ast.findFirstToken(TokenTypes.SEMI).getLineNo();
640        lastImportStatic = isStatic;
641        beforeFirstImport = false;
642    }
643
644    /**
645     * Shares processing...
646     *
647     * @param ident the import to process.
648     * @param isStatic whether the token is static or not.
649     * @param previous previous non-static but current is static (above), or
650     *                  previous static but current is non-static (under).
651     * @param line the line of the current import.
652     */
653    private void doVisitToken(FullIdent ident, boolean isStatic, boolean previous, int line) {
654        final String name = ident.getText();
655        final int groupIdx = getGroupNumber(isStatic && staticImportsApart, name);
656
657        if (groupIdx > lastGroup) {
658            if (!beforeFirstImport && line - lastImportLine < 2 && needSeparator(isStatic)) {
659                log(line, MSG_SEPARATION, name);
660            }
661        }
662        else if (groupIdx == lastGroup) {
663            doVisitTokenInSameGroup(isStatic, previous, name, line);
664        }
665        else {
666            log(line, MSG_ORDERING, name);
667        }
668        if (isSeparatorInGroup(groupIdx, isStatic, line)) {
669            log(line, MSG_SEPARATED_IN_GROUP, name);
670        }
671
672        lastGroup = groupIdx;
673        lastImport = name;
674    }
675
676    /**
677     * Checks whether import groups should be separated.
678     * @param isStatic whether the token is static or not.
679     * @return true if imports groups should be separated.
680     */
681    private boolean needSeparator(boolean isStatic) {
682        final boolean typeImportSeparator = !isStatic && separated;
683        final boolean staticImportSeparator;
684        if (staticImportsApart) {
685            staticImportSeparator = isStatic && separatedStaticGroups;
686        }
687        else {
688            staticImportSeparator = isStatic && separated;
689        }
690        final boolean separatorBetween = isStatic != lastImportStatic
691            && (separated || separatedStaticGroups) && staticImportsApart;
692
693        return typeImportSeparator || staticImportSeparator || separatorBetween;
694    }
695
696    /**
697     * Checks whether imports group separated internally.
698     * @param groupIdx group number.
699     * @param isStatic whether the token is static or not.
700     * @param line the line of the current import.
701     * @return true if imports group are separated internally.
702     */
703    private boolean isSeparatorInGroup(int groupIdx, boolean isStatic, int line) {
704        final boolean inSameGroup = groupIdx == lastGroup;
705        return (inSameGroup || !needSeparator(isStatic)) && isSeparatorBeforeImport(line);
706    }
707
708    /**
709     * Checks whether there is any separator before current import.
710     * @param line the line of the current import.
711     * @return true if there is separator before current import which isn't the first import.
712     */
713    private boolean isSeparatorBeforeImport(int line) {
714        return !beforeFirstImport && line - lastImportLine > 1;
715    }
716
717    /**
718     * Shares processing...
719     *
720     * @param isStatic whether the token is static or not.
721     * @param previous previous non-static but current is static (above), or
722     *     previous static but current is non-static (under).
723     * @param name the name of the current import.
724     * @param line the line of the current import.
725     */
726    private void doVisitTokenInSameGroup(boolean isStatic,
727            boolean previous, String name, int line) {
728        if (ordered) {
729            if (option == ImportOrderOption.INFLOW) {
730                if (isWrongOrder(name, isStatic)) {
731                    log(line, MSG_ORDERING, name);
732                }
733            }
734            else {
735                final boolean shouldFireError =
736                    // previous non-static but current is static (above)
737                    // or
738                    // previous static but current is non-static (under)
739                    previous
740                        ||
741                        // current and previous static or current and
742                        // previous non-static
743                        lastImportStatic == isStatic
744                    && isWrongOrder(name, isStatic);
745
746                if (shouldFireError) {
747                    log(line, MSG_ORDERING, name);
748                }
749            }
750        }
751    }
752
753    /**
754     * Checks whether import name is in wrong order.
755     * @param name import name.
756     * @param isStatic whether it is a static import name.
757     * @return true if import name is in wrong order.
758     */
759    private boolean isWrongOrder(String name, boolean isStatic) {
760        final boolean result;
761        if (isStatic) {
762            if (useContainerOrderingForStatic) {
763                result = compareContainerOrder(lastImport, name, caseSensitive) > 0;
764            }
765            else if (staticImportsApart) {
766                result = sortStaticImportsAlphabetically
767                    && compare(lastImport, name, caseSensitive) > 0;
768            }
769            else {
770                result = compare(lastImport, name, caseSensitive) > 0;
771            }
772        }
773        else {
774            // out of lexicographic order
775            result = compare(lastImport, name, caseSensitive) > 0;
776        }
777        return result;
778    }
779
780    /**
781     * Compares two import strings.
782     * We first compare the container of the static import, container being the type enclosing
783     * the static element being imported. When this returns 0, we compare the qualified
784     * import name. For e.g. this is what is considered to be container names:
785     * <p>
786     * import static HttpConstants.COLON     => HttpConstants
787     * import static HttpHeaders.addHeader   => HttpHeaders
788     * import static HttpHeaders.setHeader   => HttpHeaders
789     * import static HttpHeaders.Names.DATE  => HttpHeaders.Names
790     * </p>
791     * <p>
792     * According to this logic, HttpHeaders.Names would come after HttpHeaders.
793     *
794     * For more details, see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=473629#c3">
795     * static imports comparison method</a> in Eclipse.
796     * </p>
797     *
798     * @param importName1 first import name.
799     * @param importName2 second import name.
800     * @param caseSensitive whether the comparison of fully qualified import names is case
801     *                      sensitive.
802     * @return the value {@code 0} if str1 is equal to str2; a value
803     *         less than {@code 0} if str is less than the str2 (container order
804     *         or lexicographical); and a value greater than {@code 0} if str1 is greater than str2
805     *         (container order or lexicographically).
806     */
807    private static int compareContainerOrder(String importName1, String importName2,
808                                             boolean caseSensitive) {
809        final String container1 = getImportContainer(importName1);
810        final String container2 = getImportContainer(importName2);
811        final int compareContainersOrderResult;
812        if (caseSensitive) {
813            compareContainersOrderResult = container1.compareTo(container2);
814        }
815        else {
816            compareContainersOrderResult = container1.compareToIgnoreCase(container2);
817        }
818        final int result;
819        if (compareContainersOrderResult == 0) {
820            result = compare(importName1, importName2, caseSensitive);
821        }
822        else {
823            result = compareContainersOrderResult;
824        }
825        return result;
826    }
827
828    /**
829     * Extracts import container name from fully qualified import name.
830     * An import container name is the type which encloses the static element being imported.
831     * For example, HttpConstants, HttpHeaders, HttpHeaders.Names are import container names:
832     * <p>
833     * import static HttpConstants.COLON     => HttpConstants
834     * import static HttpHeaders.addHeader   => HttpHeaders
835     * import static HttpHeaders.setHeader   => HttpHeaders
836     * import static HttpHeaders.Names.DATE  => HttpHeaders.Names
837     * </p>
838     * @param qualifiedImportName fully qualified import name.
839     * @return import container name.
840     */
841    private static String getImportContainer(String qualifiedImportName) {
842        final int lastDotIndex = qualifiedImportName.lastIndexOf('.');
843        return qualifiedImportName.substring(0, lastDotIndex);
844    }
845
846    /**
847     * Finds out what group the specified import belongs to.
848     *
849     * @param isStatic whether the token is static or not.
850     * @param name the import name to find.
851     * @return group number for given import name.
852     */
853    private int getGroupNumber(boolean isStatic, String name) {
854        final Pattern[] patterns;
855        if (isStatic) {
856            patterns = staticGroups;
857        }
858        else {
859            patterns = groups;
860        }
861
862        int number = getGroupNumber(patterns, name);
863
864        if (isStatic && option == ImportOrderOption.BOTTOM) {
865            number += groups.length + 1;
866        }
867        else if (!isStatic && option == ImportOrderOption.TOP) {
868            number += staticGroups.length + 1;
869        }
870        return number;
871    }
872
873    /**
874     * Finds out what group the specified import belongs to.
875     *
876     * @param patterns groups to check.
877     * @param name the import name to find.
878     * @return group number for given import name.
879     */
880    private static int getGroupNumber(Pattern[] patterns, String name) {
881        int bestIndex = patterns.length;
882        int bestEnd = -1;
883        int bestPos = Integer.MAX_VALUE;
884
885        // find out what group this belongs in
886        // loop over patterns and get index
887        for (int i = 0; i < patterns.length; i++) {
888            final Matcher matcher = patterns[i].matcher(name);
889            if (matcher.find()) {
890                if (matcher.start() < bestPos) {
891                    bestIndex = i;
892                    bestEnd = matcher.end();
893                    bestPos = matcher.start();
894                }
895                else if (matcher.start() == bestPos && matcher.end() > bestEnd) {
896                    bestIndex = i;
897                    bestEnd = matcher.end();
898                }
899            }
900        }
901        return bestIndex;
902    }
903
904    /**
905     * Compares two strings.
906     *
907     * @param string1
908     *            the first string.
909     * @param string2
910     *            the second string.
911     * @param caseSensitive
912     *            whether the comparison is case sensitive.
913     * @return the value {@code 0} if string1 is equal to string2; a value
914     *         less than {@code 0} if string1 is lexicographically less
915     *         than the string2; and a value greater than {@code 0} if
916     *         string1 is lexicographically greater than string2.
917     */
918    private static int compare(String string1, String string2,
919            boolean caseSensitive) {
920        final int result;
921        if (caseSensitive) {
922            result = string1.compareTo(string2);
923        }
924        else {
925            result = string1.compareToIgnoreCase(string2);
926        }
927
928        return result;
929    }
930
931    /**
932     * Compiles the list of package groups and the order they should occur in the file.
933     *
934     * @param packageGroups a comma-separated list of package names/prefixes.
935     * @return array of compiled patterns.
936     */
937    private static Pattern[] compilePatterns(String... packageGroups) {
938        final Pattern[] patterns = new Pattern[packageGroups.length];
939
940        for (int i = 0; i < packageGroups.length; i++) {
941            String pkg = packageGroups[i];
942            final Pattern grp;
943
944            // if the pkg name is the wildcard, make it match zero chars
945            // from any name, so it will always be used as last resort.
946            if (WILDCARD_GROUP_NAME.equals(pkg)) {
947                // matches any package
948                grp = Pattern.compile("");
949            }
950            else if (CommonUtil.startsWithChar(pkg, '/')) {
951                if (!CommonUtil.endsWithChar(pkg, '/')) {
952                    throw new IllegalArgumentException("Invalid group: " + pkg);
953                }
954                pkg = pkg.substring(1, pkg.length() - 1);
955                grp = Pattern.compile(pkg);
956            }
957            else {
958                final StringBuilder pkgBuilder = new StringBuilder(pkg);
959                if (!CommonUtil.endsWithChar(pkg, '.')) {
960                    pkgBuilder.append('.');
961                }
962                grp = Pattern.compile("^" + Pattern.quote(pkgBuilder.toString()));
963            }
964
965            patterns[i] = grp;
966        }
967        return patterns;
968    }
969
970}