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