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 |