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