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="https://checkstyle.org/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
106 * an additional group, located at the end. Thus, the empty list of static groups (the default
107 * value) 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="https://checkstyle.org/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>
328 * <p>
329 * Example for {@code useContainerOrderingForStatic=true}
330 * </p>
331 * <pre>
332 * &lt;module name=&quot;ImportOrder&quot;&gt;
333 *   &lt;property name=&quot;useContainerOrderingForStatic&quot; value=&quot;true&quot;/&gt;
334 *   &lt;property name=&quot;ordered&quot; value=&quot;true&quot;/&gt;
335 *   &lt;property name=&quot;option&quot; value=&quot;top&quot;/&gt;
336 *   &lt;property name=&quot;caseSensitive&quot; value=&quot;false&quot;/&gt;
337 *   &lt;property name=&quot;sortStaticImportsAlphabetically&quot; value=&quot;true&quot;/&gt;
338 * &lt;/module&gt;
339 * </pre>
340 * <pre>
341 * import static io.netty.handler.codec.http.HttpConstants.COLON;
342 * import static io.netty.handler.codec.http.HttpHeaders.addHeader;
343 * import static io.netty.handler.codec.http.HttpHeaders.setHeader;
344 * import static io.netty.handler.codec.http.HttpHeaders.Names.DATE;
345 *
346 * public class InputEclipseStaticImportsOrder { }
347 * </pre>
348 * <p>
349 * Example for {@code useContainerOrderingForStatic=false}
350 * </p>
351 * <pre>
352 * &lt;module name=&quot;ImportOrder&quot;&gt;
353 *   &lt;property name=&quot;useContainerOrderingForStatic&quot; value=&quot;false&quot;/&gt;
354 *   &lt;property name=&quot;ordered&quot; value=&quot;true&quot;/&gt;
355 *   &lt;property name=&quot;option&quot; value=&quot;top&quot;/&gt;
356 *   &lt;property name=&quot;caseSensitive&quot; value=&quot;false&quot;/&gt;
357 *   &lt;property name=&quot;sortStaticImportsAlphabetically&quot; value=&quot;true&quot;/&gt;
358 * &lt;/module&gt;
359 * </pre>
360 * <pre>
361 * import static io.netty.handler.codec.http.HttpConstants.COLON;
362 * import static io.netty.handler.codec.http.HttpHeaders.addHeader;
363 * import static io.netty.handler.codec.http.HttpHeaders.setHeader;
364 * import static io.netty.handler.codec.http.HttpHeaders.Names.DATE; // violation
365 *
366 * public class InputEclipseStaticImportsOrder { }
367 * </pre>
368 *
369 * @since 3.2
370 */
371@FileStatefulCheck
372public class ImportOrderCheck
373    extends AbstractCheck {
374
375    /**
376     * A key is pointing to the warning message text in "messages.properties"
377     * file.
378     */
379    public static final String MSG_SEPARATION = "import.separation";
380
381    /**
382     * A key is pointing to the warning message text in "messages.properties"
383     * file.
384     */
385    public static final String MSG_ORDERING = "import.ordering";
386
387    /**
388     * A key is pointing to the warning message text in "messages.properties"
389     * file.
390     */
391    public static final String MSG_SEPARATED_IN_GROUP = "import.groups.separated.internally";
392
393    /** The special wildcard that catches all remaining groups. */
394    private static final String WILDCARD_GROUP_NAME = "*";
395
396    /** Empty array of pattern type needed to initialize check. */
397    private static final Pattern[] EMPTY_PATTERN_ARRAY = new Pattern[0];
398
399    /**
400     * Specify list of <b>type import</b> groups (every group identified either by a common prefix
401     * string, or by a regular expression enclosed in forward slashes (e.g. {@code /regexp/}).
402     * All type imports, which does not match any group, falls into an additional group,
403     * located at the end. Thus, the empty list of type groups (the default value) means one group
404     * for all type imports.
405     */
406    private Pattern[] groups = EMPTY_PATTERN_ARRAY;
407
408    /**
409     * Specify list of <b>static</b> import groups (every group identified either by a common prefix
410     * string, or by a regular expression enclosed in forward slashes (e.g. {@code /regexp/}).
411     * All static imports, which does not match any group, falls into an additional group, located
412     * at the end. Thus, the empty list of static groups (the default value) means one group for all
413     * static imports. This property has effect only when the property {@code option} is set to
414     * {@code top} or {@code bottom}.
415     */
416    private Pattern[] staticGroups = EMPTY_PATTERN_ARRAY;
417
418    /**
419     * Control whether type import groups should be separated by, at least, one blank
420     * line or comment and aren't separated internally. It doesn't affect separations for static
421     * imports.
422     */
423    private boolean separated;
424
425    /**
426     * Control whether static import groups should be separated by, at least, one blank
427     * line or comment and aren't separated internally. This property has effect only when the
428     * property {@code option} is is set to {@code top} or {@code bottom}.
429     */
430    private boolean separatedStaticGroups;
431
432    /**
433     * Control whether type imports within each group should be sorted.
434     * It doesn't affect sorting for static imports.
435     */
436    private boolean ordered = true;
437
438    /**
439     * Control whether string comparison should be case sensitive or not. Case sensitive
440     * sorting is in <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>.
441     * It affects both type imports and static imports.
442     */
443    private boolean caseSensitive = true;
444
445    /** Last imported group. */
446    private int lastGroup;
447    /** Line number of last import. */
448    private int lastImportLine;
449    /** Name of last import. */
450    private String lastImport;
451    /** If last import was static. */
452    private boolean lastImportStatic;
453    /** Whether there was any imports. */
454    private boolean beforeFirstImport;
455    /**
456     * Whether static and type import groups should be split apart.
457     * When the {@code option} property is set to {@code INFLOW}, {@code BELOW} or {@code UNDER},
458     * both the type and static imports use the properties {@code groups} and {@code separated}.
459     * When the {@code option} property is set to {@code TOP} or {@code BOTTOM}, static imports
460     * uses the properties {@code staticGroups} and {@code separatedStaticGroups}.
461     **/
462    private boolean staticImportsApart;
463
464    /**
465     * Control whether <b>static imports</b> located at <b>top</b> or <b>bottom</b> are
466     * sorted within the group.
467     */
468    private boolean sortStaticImportsAlphabetically;
469
470    /**
471     * Control whether to use container ordering (Eclipse IDE term) for static imports
472     * or not.
473     */
474    private boolean useContainerOrderingForStatic;
475
476    /**
477     * Specify policy on the relative order between type imports and static imports.
478     */
479    private ImportOrderOption option = ImportOrderOption.UNDER;
480
481    /**
482     * Setter to specify policy on the relative order between type imports and static imports.
483     * @param optionStr string to decode option from
484     * @throws IllegalArgumentException if unable to decode
485     */
486    public void setOption(String optionStr) {
487        option = ImportOrderOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
488    }
489
490    /**
491     * Setter to specify list of <b>type import</b> groups (every group identified either by a
492     * common prefix string, or by a regular expression enclosed in forward slashes
493     * (e.g. {@code /regexp/}). All type imports, which does not match any group, falls into an
494     * additional group, located at the end. Thus, the empty list of type groups (the default value)
495     * means one group for all type imports.
496     *
497     * @param packageGroups a comma-separated list of package names/prefixes.
498     */
499    public void setGroups(String... packageGroups) {
500        groups = compilePatterns(packageGroups);
501    }
502
503    /**
504     * Setter to specify list of <b>static</b> import groups (every group identified either by a
505     * common prefix string, or by a regular expression enclosed in forward slashes
506     * (e.g. {@code /regexp/}). All static imports, which does not match any group, falls into an
507     * additional group, located at the end. Thus, the empty list of static groups (the default
508     * value) means one group for all static imports. This property has effect only when
509     * the property {@code option} is set to {@code top} or {@code bottom}.
510     *
511     * @param packageGroups a comma-separated list of package names/prefixes.
512     */
513    public void setStaticGroups(String... packageGroups) {
514        staticGroups = compilePatterns(packageGroups);
515    }
516
517    /**
518     * Setter to control whether type imports within each group should be sorted.
519     * It doesn't affect sorting for static imports.
520     *
521     * @param ordered
522     *            whether lexicographic ordering of imports within a group
523     *            required or not.
524     */
525    public void setOrdered(boolean ordered) {
526        this.ordered = ordered;
527    }
528
529    /**
530     * Setter to control whether type import groups should be separated by, at least,
531     * one blank line or comment and aren't separated internally.
532     * It doesn't affect separations for static imports.
533     *
534     * @param separated
535     *            whether groups should be separated by one blank line or comment.
536     */
537    public void setSeparated(boolean separated) {
538        this.separated = separated;
539    }
540
541    /**
542     * Setter to control whether static import groups should be separated by, at least,
543     * one blank line or comment and aren't separated internally.
544     * This property has effect only when the property
545     * {@code option} is is set to {@code top} or {@code bottom}.
546     *
547     * @param separatedStaticGroups
548     *            whether groups should be separated by one blank line or comment.
549     */
550    public void setSeparatedStaticGroups(boolean separatedStaticGroups) {
551        this.separatedStaticGroups = separatedStaticGroups;
552    }
553
554    /**
555     * Setter to control whether string comparison should be case sensitive or not.
556     * Case sensitive sorting is in
557     * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>.
558     * It affects both type imports and static imports.
559     * @param caseSensitive
560     *            whether string comparison should be case sensitive.
561     */
562    public void setCaseSensitive(boolean caseSensitive) {
563        this.caseSensitive = caseSensitive;
564    }
565
566    /**
567     * Setter to control whether <b>static imports</b> located at <b>top</b> or
568     * <b>bottom</b> are sorted within the group.
569     * @param sortAlphabetically true or false.
570     */
571    public void setSortStaticImportsAlphabetically(boolean sortAlphabetically) {
572        sortStaticImportsAlphabetically = sortAlphabetically;
573    }
574
575    /**
576     * Setter to control whether to use container ordering (Eclipse IDE term) for static
577     * imports or not.
578     * @param useContainerOrdering whether to use container ordering for static imports or not.
579     */
580    public void setUseContainerOrderingForStatic(boolean useContainerOrdering) {
581        useContainerOrderingForStatic = useContainerOrdering;
582    }
583
584    @Override
585    public int[] getDefaultTokens() {
586        return getAcceptableTokens();
587    }
588
589    @Override
590    public int[] getAcceptableTokens() {
591        return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT};
592    }
593
594    @Override
595    public int[] getRequiredTokens() {
596        return new int[] {TokenTypes.IMPORT};
597    }
598
599    @Override
600    public void beginTree(DetailAST rootAST) {
601        lastGroup = Integer.MIN_VALUE;
602        lastImportLine = Integer.MIN_VALUE;
603        lastImport = "";
604        lastImportStatic = false;
605        beforeFirstImport = true;
606        staticImportsApart =
607            option == ImportOrderOption.TOP || option == ImportOrderOption.BOTTOM;
608    }
609
610    // -@cs[CyclomaticComplexity] SWITCH was transformed into IF-ELSE.
611    @Override
612    public void visitToken(DetailAST ast) {
613        final int line = ast.getLineNo();
614        final FullIdent ident;
615        final boolean isStatic;
616
617        if (ast.getType() == TokenTypes.IMPORT) {
618            ident = FullIdent.createFullIdentBelow(ast);
619            isStatic = false;
620        }
621        else {
622            ident = FullIdent.createFullIdent(ast.getFirstChild()
623                    .getNextSibling());
624            isStatic = true;
625        }
626
627        // using set of IF instead of SWITCH to analyze Enum options to satisfy coverage.
628        // https://github.com/checkstyle/checkstyle/issues/1387
629        if (option == ImportOrderOption.TOP || option == ImportOrderOption.ABOVE) {
630            final boolean isStaticAndNotLastImport = isStatic && !lastImportStatic;
631            doVisitToken(ident, isStatic, isStaticAndNotLastImport, line);
632        }
633        else if (option == ImportOrderOption.BOTTOM || option == ImportOrderOption.UNDER) {
634            final boolean isLastImportAndNonStatic = lastImportStatic && !isStatic;
635            doVisitToken(ident, isStatic, isLastImportAndNonStatic, line);
636        }
637        else if (option == ImportOrderOption.INFLOW) {
638            // "previous" argument is useless here
639            doVisitToken(ident, isStatic, true, line);
640        }
641        else {
642            throw new IllegalStateException(
643                    "Unexpected option for static imports: " + option);
644        }
645
646        lastImportLine = ast.findFirstToken(TokenTypes.SEMI).getLineNo();
647        lastImportStatic = isStatic;
648        beforeFirstImport = false;
649    }
650
651    /**
652     * Shares processing...
653     *
654     * @param ident the import to process.
655     * @param isStatic whether the token is static or not.
656     * @param previous previous non-static but current is static (above), or
657     *                  previous static but current is non-static (under).
658     * @param line the line of the current import.
659     */
660    private void doVisitToken(FullIdent ident, boolean isStatic, boolean previous, int line) {
661        final String name = ident.getText();
662        final int groupIdx = getGroupNumber(isStatic && staticImportsApart, name);
663
664        if (groupIdx > lastGroup) {
665            if (!beforeFirstImport && line - lastImportLine < 2 && needSeparator(isStatic)) {
666                log(line, MSG_SEPARATION, name);
667            }
668        }
669        else if (groupIdx == lastGroup) {
670            doVisitTokenInSameGroup(isStatic, previous, name, line);
671        }
672        else {
673            log(line, MSG_ORDERING, name);
674        }
675        if (isSeparatorInGroup(groupIdx, isStatic, line)) {
676            log(line, MSG_SEPARATED_IN_GROUP, name);
677        }
678
679        lastGroup = groupIdx;
680        lastImport = name;
681    }
682
683    /**
684     * Checks whether import groups should be separated.
685     * @param isStatic whether the token is static or not.
686     * @return true if imports groups should be separated.
687     */
688    private boolean needSeparator(boolean isStatic) {
689        final boolean typeImportSeparator = !isStatic && separated;
690        final boolean staticImportSeparator;
691        if (staticImportsApart) {
692            staticImportSeparator = isStatic && separatedStaticGroups;
693        }
694        else {
695            staticImportSeparator = isStatic && separated;
696        }
697        final boolean separatorBetween = isStatic != lastImportStatic
698            && (separated || separatedStaticGroups) && staticImportsApart;
699
700        return typeImportSeparator || staticImportSeparator || separatorBetween;
701    }
702
703    /**
704     * Checks whether imports group separated internally.
705     * @param groupIdx group number.
706     * @param isStatic whether the token is static or not.
707     * @param line the line of the current import.
708     * @return true if imports group are separated internally.
709     */
710    private boolean isSeparatorInGroup(int groupIdx, boolean isStatic, int line) {
711        final boolean inSameGroup = groupIdx == lastGroup;
712        return (inSameGroup || !needSeparator(isStatic)) && isSeparatorBeforeImport(line);
713    }
714
715    /**
716     * Checks whether there is any separator before current import.
717     * @param line the line of the current import.
718     * @return true if there is separator before current import which isn't the first import.
719     */
720    private boolean isSeparatorBeforeImport(int line) {
721        return !beforeFirstImport && line - lastImportLine > 1;
722    }
723
724    /**
725     * Shares processing...
726     *
727     * @param isStatic whether the token is static or not.
728     * @param previous previous non-static but current is static (above), or
729     *     previous static but current is non-static (under).
730     * @param name the name of the current import.
731     * @param line the line of the current import.
732     */
733    private void doVisitTokenInSameGroup(boolean isStatic,
734            boolean previous, String name, int line) {
735        if (ordered) {
736            if (option == ImportOrderOption.INFLOW) {
737                if (isWrongOrder(name, isStatic)) {
738                    log(line, MSG_ORDERING, name);
739                }
740            }
741            else {
742                final boolean shouldFireError =
743                    // previous non-static but current is static (above)
744                    // or
745                    // previous static but current is non-static (under)
746                    previous
747                        ||
748                        // current and previous static or current and
749                        // previous non-static
750                        lastImportStatic == isStatic
751                    && isWrongOrder(name, isStatic);
752
753                if (shouldFireError) {
754                    log(line, MSG_ORDERING, name);
755                }
756            }
757        }
758    }
759
760    /**
761     * Checks whether import name is in wrong order.
762     * @param name import name.
763     * @param isStatic whether it is a static import name.
764     * @return true if import name is in wrong order.
765     */
766    private boolean isWrongOrder(String name, boolean isStatic) {
767        final boolean result;
768        if (isStatic) {
769            if (useContainerOrderingForStatic) {
770                result = compareContainerOrder(lastImport, name, caseSensitive) > 0;
771            }
772            else if (staticImportsApart) {
773                result = sortStaticImportsAlphabetically
774                    && compare(lastImport, name, caseSensitive) > 0;
775            }
776            else {
777                result = compare(lastImport, name, caseSensitive) > 0;
778            }
779        }
780        else {
781            // out of lexicographic order
782            result = compare(lastImport, name, caseSensitive) > 0;
783        }
784        return result;
785    }
786
787    /**
788     * Compares two import strings.
789     * We first compare the container of the static import, container being the type enclosing
790     * the static element being imported. When this returns 0, we compare the qualified
791     * import name. For e.g. this is what is considered to be container names:
792     * <p>
793     * import static HttpConstants.COLON     => HttpConstants
794     * import static HttpHeaders.addHeader   => HttpHeaders
795     * import static HttpHeaders.setHeader   => HttpHeaders
796     * import static HttpHeaders.Names.DATE  => HttpHeaders.Names
797     * </p>
798     * <p>
799     * According to this logic, HttpHeaders.Names would come after HttpHeaders.
800     *
801     * For more details, see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=473629#c3">
802     * static imports comparison method</a> in Eclipse.
803     * </p>
804     *
805     * @param importName1 first import name.
806     * @param importName2 second import name.
807     * @param caseSensitive whether the comparison of fully qualified import names is case
808     *                      sensitive.
809     * @return the value {@code 0} if str1 is equal to str2; a value
810     *         less than {@code 0} if str is less than the str2 (container order
811     *         or lexicographical); and a value greater than {@code 0} if str1 is greater than str2
812     *         (container order or lexicographically).
813     */
814    private static int compareContainerOrder(String importName1, String importName2,
815                                             boolean caseSensitive) {
816        final String container1 = getImportContainer(importName1);
817        final String container2 = getImportContainer(importName2);
818        final int compareContainersOrderResult;
819        if (caseSensitive) {
820            compareContainersOrderResult = container1.compareTo(container2);
821        }
822        else {
823            compareContainersOrderResult = container1.compareToIgnoreCase(container2);
824        }
825        final int result;
826        if (compareContainersOrderResult == 0) {
827            result = compare(importName1, importName2, caseSensitive);
828        }
829        else {
830            result = compareContainersOrderResult;
831        }
832        return result;
833    }
834
835    /**
836     * Extracts import container name from fully qualified import name.
837     * An import container name is the type which encloses the static element being imported.
838     * For example, HttpConstants, HttpHeaders, HttpHeaders.Names are import container names:
839     * <p>
840     * import static HttpConstants.COLON     => HttpConstants
841     * import static HttpHeaders.addHeader   => HttpHeaders
842     * import static HttpHeaders.setHeader   => HttpHeaders
843     * import static HttpHeaders.Names.DATE  => HttpHeaders.Names
844     * </p>
845     * @param qualifiedImportName fully qualified import name.
846     * @return import container name.
847     */
848    private static String getImportContainer(String qualifiedImportName) {
849        final int lastDotIndex = qualifiedImportName.lastIndexOf('.');
850        return qualifiedImportName.substring(0, lastDotIndex);
851    }
852
853    /**
854     * Finds out what group the specified import belongs to.
855     *
856     * @param isStatic whether the token is static or not.
857     * @param name the import name to find.
858     * @return group number for given import name.
859     */
860    private int getGroupNumber(boolean isStatic, String name) {
861        final Pattern[] patterns;
862        if (isStatic) {
863            patterns = staticGroups;
864        }
865        else {
866            patterns = groups;
867        }
868
869        int number = getGroupNumber(patterns, name);
870
871        if (isStatic && option == ImportOrderOption.BOTTOM) {
872            number += groups.length + 1;
873        }
874        else if (!isStatic && option == ImportOrderOption.TOP) {
875            number += staticGroups.length + 1;
876        }
877        return number;
878    }
879
880    /**
881     * Finds out what group the specified import belongs to.
882     *
883     * @param patterns groups to check.
884     * @param name the import name to find.
885     * @return group number for given import name.
886     */
887    private static int getGroupNumber(Pattern[] patterns, String name) {
888        int bestIndex = patterns.length;
889        int bestEnd = -1;
890        int bestPos = Integer.MAX_VALUE;
891
892        // find out what group this belongs in
893        // loop over patterns and get index
894        for (int i = 0; i < patterns.length; i++) {
895            final Matcher matcher = patterns[i].matcher(name);
896            if (matcher.find()) {
897                if (matcher.start() < bestPos) {
898                    bestIndex = i;
899                    bestEnd = matcher.end();
900                    bestPos = matcher.start();
901                }
902                else if (matcher.start() == bestPos && matcher.end() > bestEnd) {
903                    bestIndex = i;
904                    bestEnd = matcher.end();
905                }
906            }
907        }
908        return bestIndex;
909    }
910
911    /**
912     * Compares two strings.
913     *
914     * @param string1
915     *            the first string.
916     * @param string2
917     *            the second string.
918     * @param caseSensitive
919     *            whether the comparison is case sensitive.
920     * @return the value {@code 0} if string1 is equal to string2; a value
921     *         less than {@code 0} if string1 is lexicographically less
922     *         than the string2; and a value greater than {@code 0} if
923     *         string1 is lexicographically greater than string2.
924     */
925    private static int compare(String string1, String string2,
926            boolean caseSensitive) {
927        final int result;
928        if (caseSensitive) {
929            result = string1.compareTo(string2);
930        }
931        else {
932            result = string1.compareToIgnoreCase(string2);
933        }
934
935        return result;
936    }
937
938    /**
939     * Compiles the list of package groups and the order they should occur in the file.
940     *
941     * @param packageGroups a comma-separated list of package names/prefixes.
942     * @return array of compiled patterns.
943     */
944    private static Pattern[] compilePatterns(String... packageGroups) {
945        final Pattern[] patterns = new Pattern[packageGroups.length];
946
947        for (int i = 0; i < packageGroups.length; i++) {
948            String pkg = packageGroups[i];
949            final Pattern grp;
950
951            // if the pkg name is the wildcard, make it match zero chars
952            // from any name, so it will always be used as last resort.
953            if (WILDCARD_GROUP_NAME.equals(pkg)) {
954                // matches any package
955                grp = Pattern.compile("");
956            }
957            else if (CommonUtil.startsWithChar(pkg, '/')) {
958                if (!CommonUtil.endsWithChar(pkg, '/')) {
959                    throw new IllegalArgumentException("Invalid group: " + pkg);
960                }
961                pkg = pkg.substring(1, pkg.length() - 1);
962                grp = Pattern.compile(pkg);
963            }
964            else {
965                final StringBuilder pkgBuilder = new StringBuilder(pkg);
966                if (!CommonUtil.endsWithChar(pkg, '.')) {
967                    pkgBuilder.append('.');
968                }
969                grp = Pattern.compile("^" + Pattern.quote(pkgBuilder.toString()));
970            }
971
972            patterns[i] = grp;
973        }
974        return patterns;
975    }
976
977}