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