| 1 | //////////////////////////////////////////////////////////////////////////////// | |
| 2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
| 3 | // Copyright (C) 2001-2017 the original author or authors. | |
| 4 | // | |
| 5 | // This library is free software; you can redistribute it and/or | |
| 6 | // modify it under the terms of the GNU Lesser General Public | |
| 7 | // License as published by the Free Software Foundation; either | |
| 8 | // version 2.1 of the License, or (at your option) any later version. | |
| 9 | // | |
| 10 | // This library is distributed in the hope that it will be useful, | |
| 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 13 | // Lesser General Public License for more details. | |
| 14 | // | |
| 15 | // You should have received a copy of the GNU Lesser General Public | |
| 16 | // License along with this library; if not, write to the Free Software | |
| 17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| 18 | //////////////////////////////////////////////////////////////////////////////// | |
| 19 | ||
| 20 | package com.puppycrawl.tools.checkstyle.checks.imports; | |
| 21 | ||
| 22 | import java.util.Locale; | |
| 23 | import java.util.regex.Matcher; | |
| 24 | import java.util.regex.Pattern; | |
| 25 | ||
| 26 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
| 27 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
| 28 | import com.puppycrawl.tools.checkstyle.api.FullIdent; | |
| 29 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
| 30 | import com.puppycrawl.tools.checkstyle.utils.CommonUtils; | |
| 31 | ||
| 32 | /** | |
| 33 | * <ul> | |
| 34 | * <li>groups imports: ensures that groups of imports come in a specific order | |
| 35 | * (e.g., java. comes first, javax. comes second, then everything else)</li> | |
| 36 | * <li>adds a separation between groups : ensures that a blank line sit between | |
| 37 | * each group</li> | |
| 38 | * <li>import groups aren't separated internally: ensures that | |
| 39 | * each group aren't separated internally by blank line or comment</li> | |
| 40 | * <li>sorts imports inside each group: ensures that imports within each group | |
| 41 | * are in lexicographic order</li> | |
| 42 | * <li>sorts according to case: ensures that the comparison between import is | |
| 43 | * case sensitive</li> | |
| 44 | * <li>groups static imports: ensures that static imports are at the top (or the | |
| 45 | * bottom) of all the imports, or above (or under) each group, or are treated | |
| 46 | * like non static imports (@see {@link ImportOrderOption}</li> | |
| 47 | * </ul>. | |
| 48 | * | |
| 49 | * <pre> | |
| 50 | * Properties: | |
| 51 | * </pre> | |
| 52 | * <table summary="Properties" border="1"> | |
| 53 | * <tr><th>name</th><th>Description</th><th>type</th><th>default value</th></tr> | |
| 54 | * <tr><td>option</td><td>policy on the relative order between regular imports and static | |
| 55 | * imports</td><td>{@link ImportOrderOption}</td><td>under</td></tr> | |
| 56 | * <tr><td>groups</td><td>list of imports groups (every group identified either by a common | |
| 57 | * prefix string, or by a regular expression enclosed in forward slashes (e.g. /regexp/)</td> | |
| 58 | * <td>list of strings</td><td>empty list</td></tr> | |
| 59 | * <tr><td>ordered</td><td>whether imports within group should be sorted</td> | |
| 60 | * <td>Boolean</td><td>true</td></tr> | |
| 61 | * <tr><td>separated</td><td>whether imports groups should be separated by, at least, | |
| 62 | * one blank line and aren't separated internally</td><td>Boolean</td><td>false</td></tr> | |
| 63 | * <tr><td>caseSensitive</td><td>whether string comparison should be case sensitive or not. | |
| 64 | * Case sensitive sorting is in ASCII sort order</td><td>Boolean</td><td>true</td></tr> | |
| 65 | * <tr><td>sortStaticImportsAlphabetically</td><td>whether static imports grouped by top or | |
| 66 | * bottom option are sorted alphabetically or not</td><td>Boolean</td><td>false</td></tr> | |
| 67 | * <tr><td>useContainerOrderingForStatic</td><td>whether to use container ordering | |
| 68 | * (Eclipse IDE term) for static imports or not</td><td>Boolean</td><td>false</td></tr> | |
| 69 | * </table> | |
| 70 | * | |
| 71 | * <p> | |
| 72 | * Example: | |
| 73 | * </p> | |
| 74 | * <p>To configure the check so that it matches default Eclipse formatter configuration | |
| 75 | * (tested on Kepler, Luna and Mars):</p> | |
| 76 | * <ul> | |
| 77 | * <li>group of static imports is on the top</li> | |
| 78 | * <li>groups of non-static imports: "java" then "javax" | |
| 79 | * packages first, then "org" and then all other imports</li> | |
| 80 | * <li>imports will be sorted in the groups</li> | |
| 81 | * <li>groups are separated by, at least, one blank line and aren't separated internally</li> | |
| 82 | * </ul> | |
| 83 | * | |
| 84 | * <pre> | |
| 85 | * <module name="ImportOrder"> | |
| 86 | * <property name="groups" value="/^javax?\./,org"/> | |
| 87 | * <property name="ordered" value="true"/> | |
| 88 | * <property name="separated" value="true"/> | |
| 89 | * <property name="option" value="above"/> | |
| 90 | * <property name="sortStaticImportsAlphabetically" value="true"/> | |
| 91 | * </module> | |
| 92 | * </pre> | |
| 93 | * | |
| 94 | * <p>To configure the check so that it matches default IntelliJ IDEA formatter configuration | |
| 95 | * (tested on v14):</p> | |
| 96 | * <ul> | |
| 97 | * <li>group of static imports is on the bottom</li> | |
| 98 | * <li>groups of non-static imports: all imports except of "javax" and | |
| 99 | * "java", then "javax" and "java"</li> | |
| 100 | * <li>imports will be sorted in the groups</li> | |
| 101 | * <li>groups are separated by, at least, one blank line and aren't separated internally</li> | |
| 102 | * </ul> | |
| 103 | * | |
| 104 | * <p> | |
| 105 | * Note: "separated" option is disabled because IDEA default has blank line | |
| 106 | * between "java" and static imports, and no blank line between | |
| 107 | * "javax" and "java" | |
| 108 | * </p> | |
| 109 | * | |
| 110 | * <pre> | |
| 111 | * <module name="ImportOrder"> | |
| 112 | * <property name="groups" value="*,javax,java"/> | |
| 113 | * <property name="ordered" value="true"/> | |
| 114 | * <property name="separated" value="false"/> | |
| 115 | * <property name="option" value="bottom"/> | |
| 116 | * <property name="sortStaticImportsAlphabetically" value="true"/> | |
| 117 | * </module> | |
| 118 | * </pre> | |
| 119 | * | |
| 120 | * <p>To configure the check so that it matches default NetBeans formatter configuration | |
| 121 | * (tested on v8):</p> | |
| 122 | * <ul> | |
| 123 | * <li>groups of non-static imports are not defined, all imports will be sorted | |
| 124 | * as a one group</li> | |
| 125 | * <li>static imports are not separated, they will be sorted along with other imports</li> | |
| 126 | * </ul> | |
| 127 | * | |
| 128 | * <pre> | |
| 129 | * <module name="ImportOrder"> | |
| 130 | * <property name="option" value="inflow"/> | |
| 131 | * </module> | |
| 132 | * </pre> | |
| 133 | * | |
| 134 | * <p> | |
| 135 | * Group descriptions enclosed in slashes are interpreted as regular | |
| 136 | * expressions. If multiple groups match, the one matching a longer | |
| 137 | * substring of the imported name will take precedence, with ties | |
| 138 | * broken first in favor of earlier matches and finally in favor of | |
| 139 | * the first matching group. | |
| 140 | * </p> | |
| 141 | * | |
| 142 | * <p> | |
| 143 | * There is always a wildcard group to which everything not in a named group | |
| 144 | * belongs. If an import does not match a named group, the group belongs to | |
| 145 | * this wildcard group. The wildcard group position can be specified using the | |
| 146 | * {@code *} character. | |
| 147 | * </p> | |
| 148 | * | |
| 149 | * <p>Check also has on option making it more flexible: | |
| 150 | * <b>sortStaticImportsAlphabetically</b> - sets whether static imports grouped by | |
| 151 | * <b>top</b> or <b>bottom</b> option should be sorted alphabetically or | |
| 152 | * not, default value is <b>false</b>. It is applied to static imports grouped | |
| 153 | * with <b>top</b> or <b>bottom</b> options.<br> | |
| 154 | * This option is helping in reconciling of this Check and other tools like | |
| 155 | * Eclipse's Organize Imports feature. | |
| 156 | * </p> | |
| 157 | * <p> | |
| 158 | * To configure the Check allows static imports grouped to the <b>top</b> | |
| 159 | * being sorted alphabetically: | |
| 160 | * </p> | |
| 161 | * | |
| 162 | * <pre> | |
| 163 | * {@code | |
| 164 | * import static java.lang.Math.abs; | |
| 165 | * import static org.abego.treelayout.Configuration.AlignmentInLevel; // OK, alphabetical order | |
| 166 | * | |
| 167 | * import org.abego.*; | |
| 168 | * | |
| 169 | * import java.util.Set; | |
| 170 | * | |
| 171 | * public class SomeClass { ... } | |
| 172 | * } | |
| 173 | * </pre> | |
| 174 | * | |
| 175 | * | |
| 176 | * @author Bill Schneider | |
| 177 | * @author o_sukhodolsky | |
| 178 | * @author David DIDIER | |
| 179 | * @author Steve McKay | |
| 180 | * @author <a href="mailto:nesterenko-aleksey@list.ru">Aleksey Nesterenko</a> | |
| 181 | * @author Andrei Selkin | |
| 182 | */ | |
| 183 | public class ImportOrderCheck | |
| 184 | extends AbstractCheck { | |
| 185 | ||
| 186 | /** | |
| 187 | * A key is pointing to the warning message text in "messages.properties" | |
| 188 | * file. | |
| 189 | */ | |
| 190 | public static final String MSG_SEPARATION = "import.separation"; | |
| 191 | ||
| 192 | /** | |
| 193 | * A key is pointing to the warning message text in "messages.properties" | |
| 194 | * file. | |
| 195 | */ | |
| 196 | public static final String MSG_ORDERING = "import.ordering"; | |
| 197 | ||
| 198 | /** | |
| 199 | * A key is pointing to the warning message text in "messages.properties" | |
| 200 | * file. | |
| 201 | */ | |
| 202 | public static final String MSG_SEPARATED_IN_GROUP = "import.groups.separated.internally"; | |
| 203 | ||
| 204 | /** The special wildcard that catches all remaining groups. */ | |
| 205 | private static final String WILDCARD_GROUP_NAME = "*"; | |
| 206 | ||
| 207 | /** Empty array of pattern type needed to initialize check. */ | |
| 208 | private static final Pattern[] EMPTY_PATTERN_ARRAY = new Pattern[0]; | |
| 209 | ||
| 210 | /** List of import groups specified by the user. */ | |
| 211 | private Pattern[] groups = EMPTY_PATTERN_ARRAY; | |
| 212 | /** Require imports in group be separated. */ | |
| 213 | private boolean separated; | |
| 214 | /** Require imports in group. */ | |
| 215 | private boolean ordered = true; | |
| 216 | /** Should comparison be case sensitive. */ | |
| 217 | private boolean caseSensitive = true; | |
| 218 | ||
| 219 | /** Last imported group. */ | |
| 220 | private int lastGroup; | |
| 221 | /** Line number of last import. */ | |
| 222 | private int lastImportLine; | |
| 223 | /** Name of last import. */ | |
| 224 | private String lastImport; | |
| 225 | /** If last import was static. */ | |
| 226 | private boolean lastImportStatic; | |
| 227 | /** Whether there was any imports. */ | |
| 228 | private boolean beforeFirstImport; | |
| 229 | /** Whether static imports should be sorted alphabetically or not. */ | |
| 230 | private boolean sortStaticImportsAlphabetically; | |
| 231 | /** Whether to use container ordering (Eclipse IDE term) for static imports or not. */ | |
| 232 | private boolean useContainerOrderingForStatic; | |
| 233 | ||
| 234 | /** The policy to enforce. */ | |
| 235 | private ImportOrderOption option = ImportOrderOption.UNDER; | |
| 236 | ||
| 237 | /** | |
| 238 | * Set the option to enforce. | |
| 239 | * @param optionStr string to decode option from | |
| 240 | * @throws IllegalArgumentException if unable to decode | |
| 241 | */ | |
| 242 | public void setOption(String optionStr) { | |
| 243 | try { | |
| 244 | option = ImportOrderOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); | |
| 245 | } | |
| 246 | catch (IllegalArgumentException iae) { | |
| 247 | throw new IllegalArgumentException("unable to parse " + optionStr, iae); | |
| 248 | } | |
| 249 | } | |
| 250 | ||
| 251 | /** | |
| 252 | * Sets the list of package groups and the order they should occur in the | |
| 253 | * file. | |
| 254 | * | |
| 255 | * @param packageGroups a comma-separated list of package names/prefixes. | |
| 256 | */ | |
| 257 | public void setGroups(String... packageGroups) { | |
| 258 | groups = new Pattern[packageGroups.length]; | |
| 259 | ||
| 260 |
3
1. setGroups : changed conditional boundary → KILLED 2. setGroups : Changed increment from 1 to -1 → KILLED 3. setGroups : negated conditional → KILLED |
for (int i = 0; i < packageGroups.length; i++) { |
| 261 | String pkg = packageGroups[i]; | |
| 262 | final Pattern grp; | |
| 263 | ||
| 264 | // if the pkg name is the wildcard, make it match zero chars | |
| 265 | // from any name, so it will always be used as last resort. | |
| 266 |
1
1. setGroups : negated conditional → KILLED |
if (WILDCARD_GROUP_NAME.equals(pkg)) { |
| 267 | // matches any package | |
| 268 | grp = Pattern.compile(""); | |
| 269 | } | |
| 270 |
1
1. setGroups : negated conditional → KILLED |
else if (CommonUtils.startsWithChar(pkg, '/')) { |
| 271 |
1
1. setGroups : negated conditional → KILLED |
if (!CommonUtils.endsWithChar(pkg, '/')) { |
| 272 | throw new IllegalArgumentException("Invalid group"); | |
| 273 | } | |
| 274 |
1
1. setGroups : Replaced integer subtraction with addition → KILLED |
pkg = pkg.substring(1, pkg.length() - 1); |
| 275 | grp = Pattern.compile(pkg); | |
| 276 | } | |
| 277 | else { | |
| 278 | final StringBuilder pkgBuilder = new StringBuilder(pkg); | |
| 279 |
1
1. setGroups : negated conditional → KILLED |
if (!CommonUtils.endsWithChar(pkg, '.')) { |
| 280 | pkgBuilder.append('.'); | |
| 281 | } | |
| 282 | grp = Pattern.compile("^" + Pattern.quote(pkgBuilder.toString())); | |
| 283 | } | |
| 284 | ||
| 285 | groups[i] = grp; | |
| 286 | } | |
| 287 | } | |
| 288 | ||
| 289 | /** | |
| 290 | * Sets whether or not imports should be ordered within any one group of | |
| 291 | * imports. | |
| 292 | * | |
| 293 | * @param ordered | |
| 294 | * whether lexicographic ordering of imports within a group | |
| 295 | * required or not. | |
| 296 | */ | |
| 297 | public void setOrdered(boolean ordered) { | |
| 298 | this.ordered = ordered; | |
| 299 | } | |
| 300 | ||
| 301 | /** | |
| 302 | * Sets whether or not groups of imports must be separated from one another | |
| 303 | * by at least one blank line. | |
| 304 | * | |
| 305 | * @param separated | |
| 306 | * whether groups should be separated by oen blank line. | |
| 307 | */ | |
| 308 | public void setSeparated(boolean separated) { | |
| 309 | this.separated = separated; | |
| 310 | } | |
| 311 | ||
| 312 | /** | |
| 313 | * Sets whether string comparison should be case sensitive or not. | |
| 314 | * | |
| 315 | * @param caseSensitive | |
| 316 | * whether string comparison should be case sensitive. | |
| 317 | */ | |
| 318 | public void setCaseSensitive(boolean caseSensitive) { | |
| 319 | this.caseSensitive = caseSensitive; | |
| 320 | } | |
| 321 | ||
| 322 | /** | |
| 323 | * Sets whether static imports (when grouped using 'top' and 'bottom' option) | |
| 324 | * are sorted alphabetically or according to the package groupings. | |
| 325 | * @param sortAlphabetically true or false. | |
| 326 | */ | |
| 327 | public void setSortStaticImportsAlphabetically(boolean sortAlphabetically) { | |
| 328 | sortStaticImportsAlphabetically = sortAlphabetically; | |
| 329 | } | |
| 330 | ||
| 331 | /** | |
| 332 | * Sets whether to use container ordering (Eclipse IDE term) for static imports or not. | |
| 333 | * @param useContainerOrdering whether to use container ordering for static imports or not. | |
| 334 | */ | |
| 335 | public void setUseContainerOrderingForStatic(boolean useContainerOrdering) { | |
| 336 | useContainerOrderingForStatic = useContainerOrdering; | |
| 337 | } | |
| 338 | ||
| 339 | @Override | |
| 340 | public int[] getDefaultTokens() { | |
| 341 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getAcceptableTokens(); |
| 342 | } | |
| 343 | ||
| 344 | @Override | |
| 345 | public int[] getAcceptableTokens() { | |
| 346 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] {TokenTypes.IMPORT, TokenTypes.STATIC_IMPORT}; |
| 347 | } | |
| 348 | ||
| 349 | @Override | |
| 350 | public int[] getRequiredTokens() { | |
| 351 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] {TokenTypes.IMPORT}; |
| 352 | } | |
| 353 | ||
| 354 | @Override | |
| 355 | public void beginTree(DetailAST rootAST) { | |
| 356 | lastGroup = Integer.MIN_VALUE; | |
| 357 | lastImportLine = Integer.MIN_VALUE; | |
| 358 | lastImport = ""; | |
| 359 | lastImportStatic = false; | |
| 360 | beforeFirstImport = true; | |
| 361 | } | |
| 362 | ||
| 363 | // -@cs[CyclomaticComplexity] SWITCH was transformed into IF-ELSE. | |
| 364 | @Override | |
| 365 | public void visitToken(DetailAST ast) { | |
| 366 | final FullIdent ident; | |
| 367 | final boolean isStatic; | |
| 368 | ||
| 369 |
1
1. visitToken : negated conditional → KILLED |
if (ast.getType() == TokenTypes.IMPORT) { |
| 370 | ident = FullIdent.createFullIdentBelow(ast); | |
| 371 | isStatic = false; | |
| 372 | } | |
| 373 | else { | |
| 374 | ident = FullIdent.createFullIdent(ast.getFirstChild() | |
| 375 | .getNextSibling()); | |
| 376 | isStatic = true; | |
| 377 | } | |
| 378 | ||
| 379 |
2
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED |
final boolean isStaticAndNotLastImport = isStatic && !lastImportStatic; |
| 380 |
2
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED |
final boolean isLastImportAndNonStatic = lastImportStatic && !isStatic; |
| 381 | ||
| 382 | // using set of IF instead of SWITCH to analyze Enum options to satisfy coverage. | |
| 383 | // https://github.com/checkstyle/checkstyle/issues/1387 | |
| 384 |
1
1. visitToken : negated conditional → KILLED |
if (option == ImportOrderOption.TOP) { |
| 385 | ||
| 386 |
1
1. visitToken : negated conditional → KILLED |
if (isLastImportAndNonStatic) { |
| 387 | lastGroup = Integer.MIN_VALUE; | |
| 388 | lastImport = ""; | |
| 389 | } | |
| 390 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitToken → KILLED |
doVisitToken(ident, isStatic, isStaticAndNotLastImport); |
| 391 | ||
| 392 | } | |
| 393 |
1
1. visitToken : negated conditional → KILLED |
else if (option == ImportOrderOption.BOTTOM) { |
| 394 | ||
| 395 |
1
1. visitToken : negated conditional → KILLED |
if (isStaticAndNotLastImport) { |
| 396 | lastGroup = Integer.MIN_VALUE; | |
| 397 | lastImport = ""; | |
| 398 | } | |
| 399 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitToken → KILLED |
doVisitToken(ident, isStatic, isLastImportAndNonStatic); |
| 400 | ||
| 401 | } | |
| 402 |
1
1. visitToken : negated conditional → KILLED |
else if (option == ImportOrderOption.ABOVE) { |
| 403 | // previous non-static but current is static | |
| 404 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitToken → KILLED |
doVisitToken(ident, isStatic, isStaticAndNotLastImport); |
| 405 | ||
| 406 | } | |
| 407 |
1
1. visitToken : negated conditional → KILLED |
else if (option == ImportOrderOption.UNDER) { |
| 408 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitToken → KILLED |
doVisitToken(ident, isStatic, isLastImportAndNonStatic); |
| 409 | ||
| 410 | } | |
| 411 |
1
1. visitToken : negated conditional → KILLED |
else if (option == ImportOrderOption.INFLOW) { |
| 412 | // "previous" argument is useless here | |
| 413 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitToken → KILLED |
doVisitToken(ident, isStatic, true); |
| 414 | ||
| 415 | } | |
| 416 | else { | |
| 417 | throw new IllegalStateException( | |
| 418 | "Unexpected option for static imports: " + option); | |
| 419 | } | |
| 420 | ||
| 421 | lastImportLine = ast.findFirstToken(TokenTypes.SEMI).getLineNo(); | |
| 422 | lastImportStatic = isStatic; | |
| 423 | beforeFirstImport = false; | |
| 424 | } | |
| 425 | ||
| 426 | /** | |
| 427 | * Shares processing... | |
| 428 | * | |
| 429 | * @param ident the import to process. | |
| 430 | * @param isStatic whether the token is static or not. | |
| 431 | * @param previous previous non-static but current is static (above), or | |
| 432 | * previous static but current is non-static (under). | |
| 433 | */ | |
| 434 | private void doVisitToken(FullIdent ident, boolean isStatic, | |
| 435 | boolean previous) { | |
| 436 | final String name = ident.getText(); | |
| 437 | final int groupIdx = getGroupNumber(name); | |
| 438 | final int line = ident.getLineNo(); | |
| 439 | ||
| 440 |
2
1. doVisitToken : negated conditional → KILLED 2. doVisitToken : negated conditional → KILLED |
if (groupIdx == lastGroup |
| 441 |
1
1. doVisitToken : negated conditional → KILLED |
|| !beforeFirstImport && isAlphabeticallySortableStaticImport(isStatic)) { |
| 442 |
1
1. doVisitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitTokenInSameGroup → KILLED |
doVisitTokenInSameGroup(isStatic, previous, name, line); |
| 443 | } | |
| 444 |
2
1. doVisitToken : changed conditional boundary → SURVIVED 2. doVisitToken : negated conditional → KILLED |
else if (groupIdx > lastGroup) { |
| 445 |
5
1. doVisitToken : changed conditional boundary → KILLED 2. doVisitToken : Replaced integer subtraction with addition → KILLED 3. doVisitToken : negated conditional → KILLED 4. doVisitToken : negated conditional → KILLED 5. doVisitToken : negated conditional → KILLED |
if (!beforeFirstImport && separated && line - lastImportLine < 2) { |
| 446 |
1
1. doVisitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(line, MSG_SEPARATION, name); |
| 447 | } | |
| 448 | } | |
| 449 | else { | |
| 450 |
1
1. doVisitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(line, MSG_ORDERING, name); |
| 451 | } | |
| 452 |
1
1. doVisitToken : negated conditional → KILLED |
if (checkSeparatorInGroup(groupIdx, isStatic, line)) { |
| 453 |
1
1. doVisitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(line, MSG_SEPARATED_IN_GROUP, name); |
| 454 | } | |
| 455 | ||
| 456 | lastGroup = groupIdx; | |
| 457 | lastImport = name; | |
| 458 | } | |
| 459 | ||
| 460 | /** | |
| 461 | * Checks whether imports group separated internally. | |
| 462 | * @param groupIdx group number. | |
| 463 | * @param isStatic whether the token is static or not. | |
| 464 | * @param line the line of the current import. | |
| 465 | * @return true if imports group are separated internally. | |
| 466 | */ | |
| 467 | private boolean checkSeparatorInGroup(int groupIdx, boolean isStatic, int line) { | |
| 468 |
8
1. checkSeparatorInGroup : negated conditional → MEMORY_ERROR 2. checkSeparatorInGroup : changed conditional boundary → KILLED 3. checkSeparatorInGroup : Replaced integer subtraction with addition → KILLED 4. checkSeparatorInGroup : negated conditional → KILLED 5. checkSeparatorInGroup : negated conditional → KILLED 6. checkSeparatorInGroup : negated conditional → KILLED 7. checkSeparatorInGroup : negated conditional → KILLED 8. checkSeparatorInGroup : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return !beforeFirstImport && separated && groupIdx == lastGroup |
| 469 | && isStatic == lastImportStatic && line - lastImportLine > 1; | |
| 470 | } | |
| 471 | ||
| 472 | /** | |
| 473 | * Checks whether static imports grouped by <b>top</b> or <b>bottom</b> option | |
| 474 | * are sorted alphabetically or not. | |
| 475 | * @param isStatic if current import is static. | |
| 476 | * @return true if static imports should be sorted alphabetically. | |
| 477 | */ | |
| 478 | private boolean isAlphabeticallySortableStaticImport(boolean isStatic) { | |
| 479 |
5
1. isAlphabeticallySortableStaticImport : negated conditional → KILLED 2. isAlphabeticallySortableStaticImport : negated conditional → KILLED 3. isAlphabeticallySortableStaticImport : negated conditional → KILLED 4. isAlphabeticallySortableStaticImport : negated conditional → KILLED 5. isAlphabeticallySortableStaticImport : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return isStatic && sortStaticImportsAlphabetically |
| 480 | && (option == ImportOrderOption.TOP | |
| 481 | || option == ImportOrderOption.BOTTOM); | |
| 482 | } | |
| 483 | ||
| 484 | /** | |
| 485 | * Shares processing... | |
| 486 | * | |
| 487 | * @param isStatic whether the token is static or not. | |
| 488 | * @param previous previous non-static but current is static (above), or | |
| 489 | * previous static but current is non-static (under). | |
| 490 | * @param name the name of the current import. | |
| 491 | * @param line the line of the current import. | |
| 492 | */ | |
| 493 | private void doVisitTokenInSameGroup(boolean isStatic, | |
| 494 | boolean previous, String name, int line) { | |
| 495 |
1
1. doVisitTokenInSameGroup : negated conditional → KILLED |
if (ordered) { |
| 496 |
1
1. doVisitTokenInSameGroup : negated conditional → KILLED |
if (option == ImportOrderOption.INFLOW) { |
| 497 |
1
1. doVisitTokenInSameGroup : negated conditional → KILLED |
if (isWrongOrder(name, isStatic)) { |
| 498 |
1
1. doVisitTokenInSameGroup : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(line, MSG_ORDERING, name); |
| 499 | } | |
| 500 | } | |
| 501 | else { | |
| 502 |
2
1. doVisitTokenInSameGroup : negated conditional → KILLED 2. doVisitTokenInSameGroup : negated conditional → KILLED |
final boolean shouldFireError = |
| 503 | // previous non-static but current is static (above) | |
| 504 | // or | |
| 505 | // previous static but current is non-static (under) | |
| 506 | previous | |
| 507 | || | |
| 508 | // current and previous static or current and | |
| 509 | // previous non-static | |
| 510 | lastImportStatic == isStatic | |
| 511 |
1
1. doVisitTokenInSameGroup : negated conditional → KILLED |
&& isWrongOrder(name, isStatic); |
| 512 | ||
| 513 |
1
1. doVisitTokenInSameGroup : negated conditional → KILLED |
if (shouldFireError) { |
| 514 |
1
1. doVisitTokenInSameGroup : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(line, MSG_ORDERING, name); |
| 515 | } | |
| 516 | } | |
| 517 | } | |
| 518 | } | |
| 519 | ||
| 520 | /** | |
| 521 | * Checks whether import name is in wrong order. | |
| 522 | * @param name import name. | |
| 523 | * @param isStatic whether it is a static import name. | |
| 524 | * @return true if import name is in wrong order. | |
| 525 | */ | |
| 526 | private boolean isWrongOrder(String name, boolean isStatic) { | |
| 527 | final boolean result; | |
| 528 |
2
1. isWrongOrder : negated conditional → KILLED 2. isWrongOrder : negated conditional → KILLED |
if (isStatic && useContainerOrderingForStatic) { |
| 529 |
2
1. isWrongOrder : changed conditional boundary → SURVIVED 2. isWrongOrder : negated conditional → KILLED |
result = compareContainerOrder(lastImport, name, caseSensitive) > 0; |
| 530 | } | |
| 531 | else { | |
| 532 | // out of lexicographic order | |
| 533 |
2
1. isWrongOrder : changed conditional boundary → SURVIVED 2. isWrongOrder : negated conditional → KILLED |
result = compare(lastImport, name, caseSensitive) > 0; |
| 534 | } | |
| 535 |
1
1. isWrongOrder : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
| 536 | } | |
| 537 | ||
| 538 | /** | |
| 539 | * Compares two import strings. | |
| 540 | * We first compare the container of the static import, container being the type enclosing | |
| 541 | * the static element being imported. When this returns 0, we compare the qualified | |
| 542 | * import name. For e.g. this is what is considered to be container names: | |
| 543 | * <p> | |
| 544 | * import static HttpConstants.COLON => HttpConstants | |
| 545 | * import static HttpHeaders.addHeader => HttpHeaders | |
| 546 | * import static HttpHeaders.setHeader => HttpHeaders | |
| 547 | * import static HttpHeaders.Names.DATE => HttpHeaders.Names | |
| 548 | * </p> | |
| 549 | * <p> | |
| 550 | * According to this logic, HttpHeaders.Names would come after HttpHeaders. | |
| 551 | * | |
| 552 | * For more details, see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=473629#c3"> | |
| 553 | * static imports comparison method</a> in Eclipse. | |
| 554 | * </p> | |
| 555 | * | |
| 556 | * @param importName1 first import name. | |
| 557 | * @param importName2 second import name. | |
| 558 | * @param caseSensitive whether the comparison of fully qualified import names is case | |
| 559 | * sensitive. | |
| 560 | * @return the value {@code 0} if str1 is equal to str2; a value | |
| 561 | * less than {@code 0} if str is less than the str2 (container order | |
| 562 | * or lexicographical); and a value greater than {@code 0} if str1 is greater than str2 | |
| 563 | * (container order or lexicographically). | |
| 564 | */ | |
| 565 | private static int compareContainerOrder(String importName1, String importName2, | |
| 566 | boolean caseSensitive) { | |
| 567 | final String container1 = getImportContainer(importName1); | |
| 568 | final String container2 = getImportContainer(importName2); | |
| 569 | final int compareContainersOrderResult; | |
| 570 |
1
1. compareContainerOrder : negated conditional → SURVIVED |
if (caseSensitive) { |
| 571 | compareContainersOrderResult = container1.compareTo(container2); | |
| 572 | } | |
| 573 | else { | |
| 574 | compareContainersOrderResult = container1.compareToIgnoreCase(container2); | |
| 575 | } | |
| 576 | final int result; | |
| 577 |
1
1. compareContainerOrder : negated conditional → KILLED |
if (compareContainersOrderResult == 0) { |
| 578 | result = compare(importName1, importName2, caseSensitive); | |
| 579 | } | |
| 580 | else { | |
| 581 | result = compareContainersOrderResult; | |
| 582 | } | |
| 583 |
1
1. compareContainerOrder : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
| 584 | } | |
| 585 | ||
| 586 | /** | |
| 587 | * Extracts import container name from fully qualified import name. | |
| 588 | * An import container name is the type which encloses the static element being imported. | |
| 589 | * For example, HttpConstants, HttpHeaders, HttpHeaders.Names are import container names: | |
| 590 | * <p> | |
| 591 | * import static HttpConstants.COLON => HttpConstants | |
| 592 | * import static HttpHeaders.addHeader => HttpHeaders | |
| 593 | * import static HttpHeaders.setHeader => HttpHeaders | |
| 594 | * import static HttpHeaders.Names.DATE => HttpHeaders.Names | |
| 595 | * </p> | |
| 596 | * @param qualifiedImportName fully qualified import name. | |
| 597 | * @return import container name. | |
| 598 | */ | |
| 599 | private static String getImportContainer(String qualifiedImportName) { | |
| 600 | final int lastDotIndex = qualifiedImportName.lastIndexOf('.'); | |
| 601 |
1
1. getImportContainer : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::getImportContainer to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return qualifiedImportName.substring(0, lastDotIndex); |
| 602 | } | |
| 603 | ||
| 604 | /** | |
| 605 | * Finds out what group the specified import belongs to. | |
| 606 | * | |
| 607 | * @param name the import name to find. | |
| 608 | * @return group number for given import name. | |
| 609 | */ | |
| 610 | private int getGroupNumber(String name) { | |
| 611 | int bestIndex = groups.length; | |
| 612 | int bestLength = -1; | |
| 613 | int bestPos = 0; | |
| 614 | ||
| 615 | // find out what group this belongs in | |
| 616 | // loop over groups and get index | |
| 617 |
3
1. getGroupNumber : changed conditional boundary → KILLED 2. getGroupNumber : Changed increment from 1 to -1 → KILLED 3. getGroupNumber : negated conditional → KILLED |
for (int i = 0; i < groups.length; i++) { |
| 618 | final Matcher matcher = groups[i].matcher(name); | |
| 619 |
1
1. getGroupNumber : negated conditional → KILLED |
while (matcher.find()) { |
| 620 |
1
1. getGroupNumber : Replaced integer subtraction with addition → KILLED |
final int length = matcher.end() - matcher.start(); |
| 621 |
3
1. getGroupNumber : changed conditional boundary → SURVIVED 2. getGroupNumber : negated conditional → SURVIVED 3. getGroupNumber : negated conditional → KILLED |
if (length > bestLength |
| 622 |
2
1. getGroupNumber : changed conditional boundary → SURVIVED 2. getGroupNumber : negated conditional → SURVIVED |
|| length == bestLength && matcher.start() < bestPos) { |
| 623 | bestIndex = i; | |
| 624 | bestLength = length; | |
| 625 | bestPos = matcher.start(); | |
| 626 | } | |
| 627 | } | |
| 628 | } | |
| 629 | ||
| 630 |
1
1. getGroupNumber : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return bestIndex; |
| 631 | } | |
| 632 | ||
| 633 | /** | |
| 634 | * Compares two strings. | |
| 635 | * | |
| 636 | * @param string1 | |
| 637 | * the first string. | |
| 638 | * @param string2 | |
| 639 | * the second string. | |
| 640 | * @param caseSensitive | |
| 641 | * whether the comparison is case sensitive. | |
| 642 | * @return the value {@code 0} if string1 is equal to string2; a value | |
| 643 | * less than {@code 0} if string1 is lexicographically less | |
| 644 | * than the string2; and a value greater than {@code 0} if | |
| 645 | * string1 is lexicographically greater than string2. | |
| 646 | */ | |
| 647 | private static int compare(String string1, String string2, | |
| 648 | boolean caseSensitive) { | |
| 649 | final int result; | |
| 650 |
1
1. compare : negated conditional → KILLED |
if (caseSensitive) { |
| 651 | result = string1.compareTo(string2); | |
| 652 | } | |
| 653 | else { | |
| 654 | result = string1.compareToIgnoreCase(string2); | |
| 655 | } | |
| 656 | ||
| 657 |
1
1. compare : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
| 658 | } | |
| 659 | } | |
Mutations | ||
| 260 |
1.1 2.2 3.3 |
|
| 266 |
1.1 |
|
| 270 |
1.1 |
|
| 271 |
1.1 |
|
| 274 |
1.1 |
|
| 279 |
1.1 |
|
| 341 |
1.1 |
|
| 346 |
1.1 |
|
| 351 |
1.1 |
|
| 369 |
1.1 |
|
| 379 |
1.1 2.2 |
|
| 380 |
1.1 2.2 |
|
| 384 |
1.1 |
|
| 386 |
1.1 |
|
| 390 |
1.1 |
|
| 393 |
1.1 |
|
| 395 |
1.1 |
|
| 399 |
1.1 |
|
| 402 |
1.1 |
|
| 404 |
1.1 |
|
| 407 |
1.1 |
|
| 408 |
1.1 |
|
| 411 |
1.1 |
|
| 413 |
1.1 |
|
| 440 |
1.1 2.2 |
|
| 441 |
1.1 |
|
| 442 |
1.1 |
|
| 444 |
1.1 2.2 |
|
| 445 |
1.1 2.2 3.3 4.4 5.5 |
|
| 446 |
1.1 |
|
| 450 |
1.1 |
|
| 452 |
1.1 |
|
| 453 |
1.1 |
|
| 468 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 |
|
| 479 |
1.1 2.2 3.3 4.4 5.5 |
|
| 495 |
1.1 |
|
| 496 |
1.1 |
|
| 497 |
1.1 |
|
| 498 |
1.1 |
|
| 502 |
1.1 2.2 |
|
| 511 |
1.1 |
|
| 513 |
1.1 |
|
| 514 |
1.1 |
|
| 528 |
1.1 2.2 |
|
| 529 |
1.1 2.2 |
|
| 533 |
1.1 2.2 |
|
| 535 |
1.1 |
|
| 570 |
1.1 |
|
| 577 |
1.1 |
|
| 583 |
1.1 |
|
| 601 |
1.1 |
|
| 617 |
1.1 2.2 3.3 |
|
| 619 |
1.1 |
|
| 620 |
1.1 |
|
| 621 |
1.1 2.2 3.3 |
|
| 622 |
1.1 2.2 |
|
| 630 |
1.1 |
|
| 650 |
1.1 |
|
| 657 |
1.1 |