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