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