View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.imports;
21  
22  import java.util.Locale;
23  import java.util.regex.Matcher;
24  import java.util.regex.Pattern;
25  
26  import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
27  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28  import com.puppycrawl.tools.checkstyle.api.DetailAST;
29  import com.puppycrawl.tools.checkstyle.api.FullIdent;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
32  import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
33  
34  /**
35   * <p>
36   * Checks the ordering/grouping of imports. Features are:
37   * </p>
38   * <ul>
39   * <li>
40   * groups type/static imports: ensures that groups of imports come in a specific order
41   * (e.g., java. comes first, javax. comes second, then everything else)
42   * </li>
43   * <li>
44   * adds a separation between type import groups : ensures that a blank line sit between each group
45   * </li>
46   * <li>
47   * type/static import groups aren't separated internally: ensures that each group aren't separated
48   * internally by blank line or comment
49   * </li>
50   * <li>
51   * sorts type/static imports inside each group: ensures that imports within each group are in
52   * lexicographic order
53   * </li>
54   * <li>
55   * sorts according to case: ensures that the comparison between imports is case-sensitive, in
56   * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>
57   * </li>
58   * <li>
59   * arrange static imports: ensures the relative order between type imports and static imports
60   * (see
61   * <a href="https://checkstyle.org/property_types.html#ImportOrderOption">ImportOrderOption</a>)
62   * </li>
63   * </ul>
64   * <ul>
65   * <li>
66   * Property {@code caseSensitive} - Control whether string comparison should be
67   * case-sensitive or not. Case-sensitive sorting is in
68   * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>.
69   * It affects both type imports and static imports.
70   * Type is {@code boolean}.
71   * Default value is {@code true}.
72   * </li>
73   * <li>
74   * Property {@code groups} - Specify list of <b>type import</b> groups. Every group identified
75   * either by a common prefix string, or by a regular expression enclosed in forward slashes
76   * (e.g. {@code /regexp/}). If an import matches two or more groups,
77   * the best match is selected (closest to the start, and the longest match).
78   * All type imports, which does not match any group, falls into an
79   * additional group, located at the end.
80   * Thus, the empty list of type groups (the default value) means one group for all type imports.
81   * Type is {@code java.lang.String[]}.
82   * Default value is {@code ""}.
83   * </li>
84   * <li>
85   * Property {@code option} - Specify policy on the relative order between type imports and static
86   * imports.
87   * Type is {@code com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderOption}.
88   * Default value is {@code under}.
89   * </li>
90   * <li>
91   * Property {@code ordered} - Control whether type imports within each group should be
92   * sorted.
93   * It doesn't affect sorting for static imports.
94   * Type is {@code boolean}.
95   * Default value is {@code true}.
96   * </li>
97   * <li>
98   * Property {@code separated} - Control whether type import groups should be separated
99   * by, at least, one blank line or comment and aren't separated internally.
100  * It doesn't affect separations for static imports.
101  * Type is {@code boolean}.
102  * Default value is {@code false}.
103  * </li>
104  * <li>
105  * Property {@code separatedStaticGroups} - Control whether static import groups should
106  * be separated by, at least, one blank line or comment and aren't separated internally.
107  * This property has effect only when the property {@code option} is set to {@code top}
108  * or {@code bottom} and when property {@code staticGroups} is enabled.
109  * Type is {@code boolean}.
110  * Default value is {@code false}.
111  * </li>
112  * <li>
113  * Property {@code sortStaticImportsAlphabetically} - Control whether
114  * <b>static imports</b> located at <b>top</b> or <b>bottom</b> are sorted within the group.
115  * Type is {@code boolean}.
116  * Default value is {@code false}.
117  * </li>
118  * <li>
119  * Property {@code staticGroups} - Specify list of <b>static</b> import groups. Every group
120  * identified either by a common prefix string, or by a regular expression enclosed in forward
121  * slashes (e.g. {@code /regexp/}). If an import matches two or more groups,
122  * the best match is selected (closest to the start, and the longest match).
123  * All static imports, which does not match any group, fall into
124  * an additional group, located at the end. Thus, the empty list of static groups (the default
125  * value) means one group for all static imports. This property has effect only when the property
126  * {@code option} is set to {@code top} or {@code bottom}.
127  * Type is {@code java.lang.String[]}.
128  * Default value is {@code ""}.
129  * </li>
130  * <li>
131  * Property {@code useContainerOrderingForStatic} - Control whether to use container
132  * ordering (Eclipse IDE term) for static imports or not.
133  * Type is {@code boolean}.
134  * Default value is {@code false}.
135  * </li>
136  * </ul>
137  * <p>
138  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
139  * </p>
140  * <p>
141  * Violation Message Keys:
142  * </p>
143  * <ul>
144  * <li>
145  * {@code import.groups.separated.internally}
146  * </li>
147  * <li>
148  * {@code import.ordering}
149  * </li>
150  * <li>
151  * {@code import.separation}
152  * </li>
153  * </ul>
154  *
155  * @since 3.2
156  */
157 @FileStatefulCheck
158 public class ImportOrderCheck
159     extends AbstractCheck {
160 
161     /**
162      * A key is pointing to the warning message text in "messages.properties"
163      * file.
164      */
165     public static final String MSG_SEPARATION = "import.separation";
166 
167     /**
168      * A key is pointing to the warning message text in "messages.properties"
169      * file.
170      */
171     public static final String MSG_ORDERING = "import.ordering";
172 
173     /**
174      * A key is pointing to the warning message text in "messages.properties"
175      * file.
176      */
177     public static final String MSG_SEPARATED_IN_GROUP = "import.groups.separated.internally";
178 
179     /** The special wildcard that catches all remaining groups. */
180     private static final String WILDCARD_GROUP_NAME = "*";
181 
182     /** The Forward slash. */
183     private static final String FORWARD_SLASH = "/";
184 
185     /** Empty array of pattern type needed to initialize check. */
186     private static final Pattern[] EMPTY_PATTERN_ARRAY = new Pattern[0];
187 
188     /**
189      * Specify list of <b>type import</b> groups. Every group identified either by a common prefix
190      * string, or by a regular expression enclosed in forward slashes (e.g. {@code /regexp/}).
191      * If an import matches two or more groups,
192      * the best match is selected (closest to the start, and the longest match).
193      * All type imports, which does not match any group, falls into an additional group,
194      * located at the end. Thus, the empty list of type groups (the default value) means one group
195      * for all type imports.
196      */
197     private String[] groups = CommonUtil.EMPTY_STRING_ARRAY;
198 
199     /**
200      * Specify list of <b>static</b> import groups. Every group identified either by a common prefix
201      * string, or by a regular expression enclosed in forward slashes (e.g. {@code /regexp/}).
202      * If an import matches two or more groups,
203      * the best match is selected (closest to the start, and the longest match).
204      * All static imports, which does not match any group, fall into an additional group, located
205      * at the end. Thus, the empty list of static groups (the default value) means one group for all
206      * static imports. This property has effect only when the property {@code option} is set to
207      * {@code top} or {@code bottom}.
208      */
209     private String[] staticGroups = CommonUtil.EMPTY_STRING_ARRAY;
210 
211     /**
212      * Control whether type import groups should be separated by, at least, one blank
213      * line or comment and aren't separated internally. It doesn't affect separations for static
214      * imports.
215      */
216     private boolean separated;
217 
218     /**
219      * Control whether static import groups should be separated by, at least, one blank
220      * line or comment and aren't separated internally. This property has effect only when the
221      * property {@code option} is set to {@code top} or {@code bottom} and when property
222      * {@code staticGroups} is enabled.
223      */
224     private boolean separatedStaticGroups;
225 
226     /**
227      * Control whether type imports within each group should be sorted.
228      * It doesn't affect sorting for static imports.
229      */
230     private boolean ordered = true;
231 
232     /**
233      * Control whether string comparison should be case-sensitive or not. Case-sensitive
234      * sorting is in <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>.
235      * It affects both type imports and static imports.
236      */
237     private boolean caseSensitive = true;
238 
239     /** Last imported group. */
240     private int lastGroup;
241     /** Line number of last import. */
242     private int lastImportLine;
243     /** Name of last import. */
244     private String lastImport;
245     /** If last import was static. */
246     private boolean lastImportStatic;
247     /** Whether there were any imports. */
248     private boolean beforeFirstImport;
249     /**
250      * Whether static and type import groups should be split apart.
251      * When the {@code option} property is set to {@code INFLOW}, {@code ABOVE} or {@code UNDER},
252      * both the type and static imports use the properties {@code groups} and {@code separated}.
253      * When the {@code option} property is set to {@code TOP} or {@code BOTTOM}, static imports
254      * uses the properties {@code staticGroups} and {@code separatedStaticGroups}.
255      **/
256     private boolean staticImportsApart;
257 
258     /**
259      * Control whether <b>static imports</b> located at <b>top</b> or <b>bottom</b> are
260      * sorted within the group.
261      */
262     private boolean sortStaticImportsAlphabetically;
263 
264     /**
265      * Control whether to use container ordering (Eclipse IDE term) for static imports
266      * or not.
267      */
268     private boolean useContainerOrderingForStatic;
269 
270     /**
271      * Specify policy on the relative order between type imports and static imports.
272      */
273     private ImportOrderOption option = ImportOrderOption.UNDER;
274 
275     /**
276      * Complied array of patterns for property {@code groups}.
277      */
278     private Pattern[] groupsReg = EMPTY_PATTERN_ARRAY;
279 
280     /**
281      * Complied array of patterns for property {@code staticGroups}.
282      */
283     private Pattern[] staticGroupsReg = EMPTY_PATTERN_ARRAY;
284 
285     /**
286      * Setter to specify policy on the relative order between type imports and static imports.
287      *
288      * @param optionStr string to decode option from
289      * @throws IllegalArgumentException if unable to decode
290      * @since 5.0
291      */
292     public void setOption(String optionStr) {
293         option = ImportOrderOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH));
294     }
295 
296     /**
297      * Setter to specify list of <b>type import</b> groups. Every group identified either by a
298      * common prefix string, or by a regular expression enclosed in forward slashes
299      * (e.g. {@code /regexp/}). If an import matches two or more groups,
300      * the best match is selected (closest to the start, and the longest match).
301      * All type imports, which does not match any group, falls into an
302      * additional group, located at the end. Thus, the empty list of type groups (the default value)
303      * means one group for all type imports.
304      *
305      * @param packageGroups a comma-separated list of package names/prefixes.
306      * @since 3.2
307      */
308     public void setGroups(String... packageGroups) {
309         groups = UnmodifiableCollectionUtil.copyOfArray(packageGroups, packageGroups.length);
310         groupsReg = compilePatterns(packageGroups);
311     }
312 
313     /**
314      * Setter to specify list of <b>static</b> import groups. Every group identified either by a
315      * common prefix string, or by a regular expression enclosed in forward slashes
316      * (e.g. {@code /regexp/}). If an import matches two or more groups,
317      * the best match is selected (closest to the start, and the longest match).
318      * All static imports, which does not match any group, fall into an
319      * additional group, located at the end. Thus, the empty list of static groups (the default
320      * value) means one group for all static imports. This property has effect only when
321      * the property {@code option} is set to {@code top} or {@code bottom}.
322      *
323      * @param packageGroups a comma-separated list of package names/prefixes.
324      * @since 8.12
325      */
326     public void setStaticGroups(String... packageGroups) {
327         staticGroups = UnmodifiableCollectionUtil.copyOfArray(packageGroups, packageGroups.length);
328         staticGroupsReg = compilePatterns(packageGroups);
329     }
330 
331     /**
332      * Setter to control whether type imports within each group should be sorted.
333      * It doesn't affect sorting for static imports.
334      *
335      * @param ordered
336      *            whether lexicographic ordering of imports within a group
337      *            required or not.
338      * @since 3.2
339      */
340     public void setOrdered(boolean ordered) {
341         this.ordered = ordered;
342     }
343 
344     /**
345      * Setter to control whether type import groups should be separated by, at least,
346      * one blank line or comment and aren't separated internally.
347      * It doesn't affect separations for static imports.
348      *
349      * @param separated
350      *            whether groups should be separated by one blank line or comment.
351      * @since 3.2
352      */
353     public void setSeparated(boolean separated) {
354         this.separated = separated;
355     }
356 
357     /**
358      * Setter to control whether static import groups should be separated by, at least,
359      * one blank line or comment and aren't separated internally.
360      * This property has effect only when the property
361      * {@code option} is set to {@code top} or {@code bottom} and when property {@code staticGroups}
362      * is enabled.
363      *
364      * @param separatedStaticGroups
365      *            whether groups should be separated by one blank line or comment.
366      * @since 8.12
367      */
368     public void setSeparatedStaticGroups(boolean separatedStaticGroups) {
369         this.separatedStaticGroups = separatedStaticGroups;
370     }
371 
372     /**
373      * Setter to control whether string comparison should be case-sensitive or not.
374      * Case-sensitive sorting is in
375      * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>.
376      * It affects both type imports and static imports.
377      *
378      * @param caseSensitive
379      *            whether string comparison should be case-sensitive.
380      * @since 3.3
381      */
382     public void setCaseSensitive(boolean caseSensitive) {
383         this.caseSensitive = caseSensitive;
384     }
385 
386     /**
387      * Setter to control whether <b>static imports</b> located at <b>top</b> or
388      * <b>bottom</b> are sorted within the group.
389      *
390      * @param sortAlphabetically true or false.
391      * @since 6.5
392      */
393     public void setSortStaticImportsAlphabetically(boolean sortAlphabetically) {
394         sortStaticImportsAlphabetically = sortAlphabetically;
395     }
396 
397     /**
398      * Setter to control whether to use container ordering (Eclipse IDE term) for static
399      * imports or not.
400      *
401      * @param useContainerOrdering whether to use container ordering for static imports or not.
402      * @since 7.1
403      */
404     public void setUseContainerOrderingForStatic(boolean useContainerOrdering) {
405         useContainerOrderingForStatic = useContainerOrdering;
406     }
407 
408     @Override
409     public int[] getDefaultTokens() {
410         return getRequiredTokens();
411     }
412 
413     @Override
414     public int[] getAcceptableTokens() {
415         return getRequiredTokens();
416     }
417 
418     @Override
419     public int[] getRequiredTokens() {
420         return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT};
421     }
422 
423     @Override
424     public void beginTree(DetailAST rootAST) {
425         lastGroup = Integer.MIN_VALUE;
426         lastImportLine = Integer.MIN_VALUE;
427         lastImportStatic = false;
428         beforeFirstImport = true;
429         staticImportsApart =
430             option == ImportOrderOption.TOP || option == ImportOrderOption.BOTTOM;
431     }
432 
433     // -@cs[CyclomaticComplexity] SWITCH was transformed into IF-ELSE.
434     @Override
435     public void visitToken(DetailAST ast) {
436         final FullIdent ident;
437         final boolean isStatic;
438 
439         if (ast.getType() == TokenTypes.IMPORT) {
440             ident = FullIdent.createFullIdentBelow(ast);
441             isStatic = false;
442         }
443         else {
444             ident = FullIdent.createFullIdent(ast.getFirstChild()
445                     .getNextSibling());
446             isStatic = true;
447         }
448 
449         // using set of IF instead of SWITCH to analyze Enum options to satisfy coverage.
450         // https://github.com/checkstyle/checkstyle/issues/1387
451         if (option == ImportOrderOption.TOP || option == ImportOrderOption.ABOVE) {
452             final boolean isStaticAndNotLastImport = isStatic && !lastImportStatic;
453             doVisitToken(ident, isStatic, isStaticAndNotLastImport, ast);
454         }
455         else if (option == ImportOrderOption.BOTTOM || option == ImportOrderOption.UNDER) {
456             final boolean isLastImportAndNonStatic = lastImportStatic && !isStatic;
457             doVisitToken(ident, isStatic, isLastImportAndNonStatic, ast);
458         }
459         else if (option == ImportOrderOption.INFLOW) {
460             // "previous" argument is useless here
461             doVisitToken(ident, isStatic, true, ast);
462         }
463         else {
464             throw new IllegalStateException(
465                     "Unexpected option for static imports: " + option);
466         }
467 
468         lastImportLine = ast.findFirstToken(TokenTypes.SEMI).getLineNo();
469         lastImportStatic = isStatic;
470         beforeFirstImport = false;
471     }
472 
473     /**
474      * Shares processing...
475      *
476      * @param ident the import to process.
477      * @param isStatic whether the token is static or not.
478      * @param previous previous non-static but current is static (above), or
479      *                  previous static but current is non-static (under).
480      * @param ast node of the AST.
481      */
482     private void doVisitToken(FullIdent ident, boolean isStatic, boolean previous, DetailAST ast) {
483         final String name = ident.getText();
484         final int groupIdx = getGroupNumber(isStatic && staticImportsApart, name);
485 
486         if (groupIdx > lastGroup) {
487             if (!beforeFirstImport
488                 && ast.getLineNo() - lastImportLine < 2
489                 && needSeparator(isStatic)) {
490                 log(ast, MSG_SEPARATION, name);
491             }
492         }
493         else if (groupIdx == lastGroup) {
494             doVisitTokenInSameGroup(isStatic, previous, name, ast);
495         }
496         else {
497             log(ast, MSG_ORDERING, name);
498         }
499         if (isSeparatorInGroup(groupIdx, isStatic, ast.getLineNo())) {
500             log(ast, MSG_SEPARATED_IN_GROUP, name);
501         }
502 
503         lastGroup = groupIdx;
504         lastImport = name;
505     }
506 
507     /**
508      * Checks whether import groups should be separated.
509      *
510      * @param isStatic whether the token is static or not.
511      * @return true if imports groups should be separated.
512      */
513     private boolean needSeparator(boolean isStatic) {
514         final boolean typeImportSeparator = !isStatic && separated;
515         final boolean staticImportSeparator;
516         if (staticImportsApart) {
517             staticImportSeparator = isStatic && separatedStaticGroups;
518         }
519         else {
520             staticImportSeparator = separated;
521         }
522         final boolean separatorBetween = isStatic != lastImportStatic
523             && (separated || separatedStaticGroups);
524 
525         return typeImportSeparator || staticImportSeparator || separatorBetween;
526     }
527 
528     /**
529      * Checks whether imports group separated internally.
530      *
531      * @param groupIdx group number.
532      * @param isStatic whether the token is static or not.
533      * @param line the line of the current import.
534      * @return true if imports group are separated internally.
535      */
536     private boolean isSeparatorInGroup(int groupIdx, boolean isStatic, int line) {
537         final boolean inSameGroup = groupIdx == lastGroup;
538         return (inSameGroup || !needSeparator(isStatic)) && isSeparatorBeforeImport(line);
539     }
540 
541     /**
542      * Checks whether there is any separator before current import.
543      *
544      * @param line the line of the current import.
545      * @return true if there is separator before current import which isn't the first import.
546      */
547     private boolean isSeparatorBeforeImport(int line) {
548         return line - lastImportLine > 1;
549     }
550 
551     /**
552      * Shares processing...
553      *
554      * @param isStatic whether the token is static or not.
555      * @param previous previous non-static but current is static (above), or
556      *     previous static but current is non-static (under).
557      * @param name the name of the current import.
558      * @param ast node of the AST.
559      */
560     private void doVisitTokenInSameGroup(boolean isStatic,
561             boolean previous, String name, DetailAST ast) {
562         if (ordered) {
563             if (option == ImportOrderOption.INFLOW) {
564                 if (isWrongOrder(name, isStatic)) {
565                     log(ast, MSG_ORDERING, name);
566                 }
567             }
568             else {
569                 final boolean shouldFireError =
570                     // previous non-static but current is static (above)
571                     // or
572                     // previous static but current is non-static (under)
573                     previous
574                         ||
575                         // current and previous static or current and
576                         // previous non-static
577                         lastImportStatic == isStatic
578                     && isWrongOrder(name, isStatic);
579 
580                 if (shouldFireError) {
581                     log(ast, MSG_ORDERING, name);
582                 }
583             }
584         }
585     }
586 
587     /**
588      * Checks whether import name is in wrong order.
589      *
590      * @param name import name.
591      * @param isStatic whether it is a static import name.
592      * @return true if import name is in wrong order.
593      */
594     private boolean isWrongOrder(String name, boolean isStatic) {
595         final boolean result;
596         if (isStatic) {
597             if (useContainerOrderingForStatic) {
598                 result = compareContainerOrder(lastImport, name, caseSensitive) > 0;
599             }
600             else if (staticImportsApart) {
601                 result = sortStaticImportsAlphabetically
602                     && compare(lastImport, name, caseSensitive) > 0;
603             }
604             else {
605                 result = compare(lastImport, name, caseSensitive) > 0;
606             }
607         }
608         else {
609             // out of lexicographic order
610             result = compare(lastImport, name, caseSensitive) > 0;
611         }
612         return result;
613     }
614 
615     /**
616      * Compares two import strings.
617      * We first compare the container of the static import, container being the type enclosing
618      * the static element being imported. When this returns 0, we compare the qualified
619      * import name. For e.g. this is what is considered to be container names:
620      * <pre>
621      * import static HttpConstants.COLON     =&gt; HttpConstants
622      * import static HttpHeaders.addHeader   =&gt; HttpHeaders
623      * import static HttpHeaders.setHeader   =&gt; HttpHeaders
624      * import static HttpHeaders.Names.DATE  =&gt; HttpHeaders.Names
625      * </pre>
626      * <p>
627      * According to this logic, HttpHeaders.Names would come after HttpHeaders.
628      * For more details, see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=473629#c3">
629      * static imports comparison method</a> in Eclipse.
630      * </p>
631      *
632      * @param importName1 first import name
633      * @param importName2 second import name
634      * @param caseSensitive whether the comparison of fully qualified import names is
635      *                      case-sensitive
636      * @return the value {@code 0} if str1 is equal to str2; a value
637      *         less than {@code 0} if str is less than the str2 (container order
638      *         or lexicographical); and a value greater than {@code 0} if str1 is greater than str2
639      *         (container order or lexicographically)
640      */
641     private static int compareContainerOrder(String importName1, String importName2,
642                                              boolean caseSensitive) {
643         final String container1 = getImportContainer(importName1);
644         final String container2 = getImportContainer(importName2);
645         final int compareContainersOrderResult;
646         if (caseSensitive) {
647             compareContainersOrderResult = container1.compareTo(container2);
648         }
649         else {
650             compareContainersOrderResult = container1.compareToIgnoreCase(container2);
651         }
652         final int result;
653         if (compareContainersOrderResult == 0) {
654             result = compare(importName1, importName2, caseSensitive);
655         }
656         else {
657             result = compareContainersOrderResult;
658         }
659         return result;
660     }
661 
662     /**
663      * Extracts import container name from fully qualified import name.
664      * An import container name is the type which encloses the static element being imported.
665      * For example, HttpConstants, HttpHeaders, HttpHeaders.Names are import container names:
666      * <pre>
667      * import static HttpConstants.COLON     =&gt; HttpConstants
668      * import static HttpHeaders.addHeader   =&gt; HttpHeaders
669      * import static HttpHeaders.setHeader   =&gt; HttpHeaders
670      * import static HttpHeaders.Names.DATE  =&gt; HttpHeaders.Names
671      * </pre>
672      *
673      * @param qualifiedImportName fully qualified import name.
674      * @return import container name.
675      */
676     private static String getImportContainer(String qualifiedImportName) {
677         final int lastDotIndex = qualifiedImportName.lastIndexOf('.');
678         return qualifiedImportName.substring(0, lastDotIndex);
679     }
680 
681     /**
682      * Finds out what group the specified import belongs to.
683      *
684      * @param isStatic whether the token is static or not.
685      * @param name the import name to find.
686      * @return group number for given import name.
687      */
688     private int getGroupNumber(boolean isStatic, String name) {
689         final Pattern[] patterns;
690         if (isStatic) {
691             patterns = staticGroupsReg;
692         }
693         else {
694             patterns = groupsReg;
695         }
696 
697         int number = getGroupNumber(patterns, name);
698 
699         if (isStatic && option == ImportOrderOption.BOTTOM) {
700             number += groups.length + 1;
701         }
702         else if (!isStatic && option == ImportOrderOption.TOP) {
703             number += staticGroups.length + 1;
704         }
705         return number;
706     }
707 
708     /**
709      * Finds out what group the specified import belongs to.
710      *
711      * @param patterns groups to check.
712      * @param name the import name to find.
713      * @return group number for given import name.
714      */
715     private static int getGroupNumber(Pattern[] patterns, String name) {
716         int bestIndex = patterns.length;
717         int bestEnd = -1;
718         int bestPos = Integer.MAX_VALUE;
719 
720         // find out what group this belongs in
721         // loop over patterns and get index
722         for (int i = 0; i < patterns.length; i++) {
723             final Matcher matcher = patterns[i].matcher(name);
724             if (matcher.find()) {
725                 if (matcher.start() < bestPos) {
726                     bestIndex = i;
727                     bestEnd = matcher.end();
728                     bestPos = matcher.start();
729                 }
730                 else if (matcher.start() == bestPos && matcher.end() > bestEnd) {
731                     bestIndex = i;
732                     bestEnd = matcher.end();
733                 }
734             }
735         }
736         return bestIndex;
737     }
738 
739     /**
740      * Compares two strings.
741      *
742      * @param string1
743      *            the first string
744      * @param string2
745      *            the second string
746      * @param caseSensitive
747      *            whether the comparison is case-sensitive
748      * @return the value {@code 0} if string1 is equal to string2; a value
749      *         less than {@code 0} if string1 is lexicographically less
750      *         than the string2; and a value greater than {@code 0} if
751      *         string1 is lexicographically greater than string2
752      */
753     private static int compare(String string1, String string2,
754             boolean caseSensitive) {
755         final int result;
756         if (caseSensitive) {
757             result = string1.compareTo(string2);
758         }
759         else {
760             result = string1.compareToIgnoreCase(string2);
761         }
762 
763         return result;
764     }
765 
766     /**
767      * Compiles the list of package groups and the order they should occur in the file.
768      *
769      * @param packageGroups a comma-separated list of package names/prefixes.
770      * @return array of compiled patterns.
771      * @throws IllegalArgumentException if any of the package groups are not valid.
772      */
773     private static Pattern[] compilePatterns(String... packageGroups) {
774         final Pattern[] patterns = new Pattern[packageGroups.length];
775         for (int i = 0; i < packageGroups.length; i++) {
776             String pkg = packageGroups[i];
777             final Pattern grp;
778 
779             // if the pkg name is the wildcard, make it match zero chars
780             // from any name, so it will always be used as last resort.
781             if (WILDCARD_GROUP_NAME.equals(pkg)) {
782                 // matches any package
783                 grp = Pattern.compile("");
784             }
785             else if (pkg.startsWith(FORWARD_SLASH)) {
786                 if (!pkg.endsWith(FORWARD_SLASH)) {
787                     throw new IllegalArgumentException("Invalid group: " + pkg);
788                 }
789                 pkg = pkg.substring(1, pkg.length() - 1);
790                 grp = Pattern.compile(pkg);
791             }
792             else {
793                 final StringBuilder pkgBuilder = new StringBuilder(pkg);
794                 if (!pkg.endsWith(".")) {
795                     pkgBuilder.append('.');
796                 }
797                 grp = Pattern.compile("^" + Pattern.quote(pkgBuilder.toString()));
798             }
799 
800             patterns[i] = grp;
801         }
802         return patterns;
803     }
804 
805 }