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