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