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 * <module name="ImportOrder"/> 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 * <module name="ImportOrder"> 197 * <property name="groups" value="/^java\./,javax,org"/> 198 * <property name="ordered" value="true"/> 199 * <property name="separated" value="true"/> 200 * <property name="option" value="above"/> 201 * <property name="sortStaticImportsAlphabetically" value="true"/> 202 * </module> 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' < '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 * <module name="ImportOrder"> 244 * <property name="groups" value="/^java\./,javax,org,com"/> 245 * <property name="ordered" value="true"/> 246 * <property name="separated" value="true"/> 247 * <property name="option" value="above"/> 248 * <property name="sortStaticImportsAlphabetically" value="true"/> 249 * </module> 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' < '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' < '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 * <module name="ImportOrder"> 306 * <property name="groups" value="*,javax,java"/> 307 * <property name="ordered" value="true"/> 308 * <property name="separated" value="false"/> 309 * <property name="option" value="bottom"/> 310 * <property name="sortStaticImportsAlphabetically" value="true"/> 311 * </module> 312 * <module name="SuppressionXpathSingleFilter"> 313 * <property name="checks" value="ImportOrder"/> 314 * <property name="message" value="^'java\..*'.*"/> 315 * </module> 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 * <module name="ImportOrder"> 346 * <property name="option" value="inflow"/> 347 * </module> 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 * <module name="ImportOrder"> 385 * <property name="sortStaticImportsAlphabetically" value="true"/> 386 * <property name="option" value="top"/> 387 * </module> 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' < '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 * <module name="ImportOrder"> 405 * <property name="staticGroups" value="org,java"/> 406 * <property name="sortStaticImportsAlphabetically" value="true"/> 407 * <property name="option" value="top"/> 408 * </module> 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 => HttpConstants 428 * import static HttpHeaders.addHeader => HttpHeaders 429 * import static HttpHeaders.setHeader => HttpHeaders 430 * import static HttpHeaders.Names.DATE => 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 * <module name="ImportOrder"> 439 * <property name="useContainerOrderingForStatic" value="true"/> 440 * <property name="ordered" value="true"/> 441 * <property name="option" value="top"/> 442 * <property name="caseSensitive" value="false"/> 443 * <property name="sortStaticImportsAlphabetically" value="true"/> 444 * </module> 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 * <module name="ImportOrder"> 459 * <property name="useContainerOrderingForStatic" value="false"/> 460 * <property name="ordered" value="true"/> 461 * <property name="option" value="top"/> 462 * <property name="caseSensitive" value="false"/> 463 * <property name="sortStaticImportsAlphabetically" value="true"/> 464 * </module> 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 * <module name="ImportOrder"> 482 * <property name="staticGroups" value="*,java,javax,org"/> 483 * <property name="option" value="top"/> 484 * <property name="separatedStaticGroups" value="true"/> 485 * </module> 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="" 499 * represents all imports as {@code everything else} group: 500 * </p> 501 * <pre> 502 * <module name="ImportOrder"> 503 * <property name="staticGroups" value=""/> 504 * <property name="option" value="top"/> 505 * </module> 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="java, javax" 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 * <module name="ImportOrder"> 523 * <property name="staticGroups" value="java, javax"/> 524 * <property name="option" value="top"/> 525 * </module> 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 => HttpConstants 989 * import static HttpHeaders.addHeader => HttpHeaders 990 * import static HttpHeaders.setHeader => HttpHeaders 991 * import static HttpHeaders.Names.DATE => 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 => HttpConstants 1036 * import static HttpHeaders.addHeader => HttpHeaders 1037 * import static HttpHeaders.setHeader => HttpHeaders 1038 * import static HttpHeaders.Names.DATE => 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}