1 | //////////////////////////////////////////////////////////////////////////////// | |
2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
3 | // Copyright (C) 2001-2021 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.FileStatefulCheck; | |
27 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
28 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
29 | import com.puppycrawl.tools.checkstyle.api.FullIdent; | |
30 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
31 | import com.puppycrawl.tools.checkstyle.utils.CommonUtil; | |
32 | ||
33 | /** | |
34 | * <p> | |
35 | * Checks the ordering/grouping of imports. Features are: | |
36 | * </p> | |
37 | * <ul> | |
38 | * <li> | |
39 | * groups type/static imports: ensures that groups of imports come in a specific order | |
40 | * (e.g., java. comes first, javax. comes second, then everything else) | |
41 | * </li> | |
42 | * <li> | |
43 | * adds a separation between type import groups : ensures that a blank line sit between each group | |
44 | * </li> | |
45 | * <li> | |
46 | * type/static import groups aren't separated internally: ensures that each group aren't separated | |
47 | * internally by blank line or comment | |
48 | * </li> | |
49 | * <li> | |
50 | * sorts type/static imports inside each group: ensures that imports within each group are in | |
51 | * lexicographic order | |
52 | * </li> | |
53 | * <li> | |
54 | * sorts according to case: ensures that the comparison between imports is case sensitive, in | |
55 | * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a> | |
56 | * </li> | |
57 | * <li> | |
58 | * arrange static imports: ensures the relative order between type imports and static imports | |
59 | * (see | |
60 | * <a href="https://checkstyle.org/property_types.html#ImportOrderOption">ImportOrderOption</a>) | |
61 | * </li> | |
62 | * </ul> | |
63 | * <ul> | |
64 | * <li> | |
65 | * Property {@code option} - specify policy on the relative order between type imports and static | |
66 | * imports. | |
67 | * Type is {@code com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderOption}. | |
68 | * Default value is {@code under}. | |
69 | * </li> | |
70 | * <li> | |
71 | * Property {@code groups} - specify list of <b>type import</b> groups (every group identified | |
72 | * either by a common prefix string, or by a regular expression enclosed in forward slashes | |
73 | * (e.g. {@code /regexp/}). All type imports, which does not match any group, falls into an | |
74 | * additional group, located at the end. | |
75 | * Thus, the empty list of type groups (the default value) means one group for all type imports. | |
76 | * Type is {@code java.lang.String[]}. | |
77 | * Validation type is {@code java.util.regex.Pattern}. | |
78 | * Default value is {@code ""}. | |
79 | * </li> | |
80 | * <li> | |
81 | * Property {@code ordered} - control whether type imports within each group should be | |
82 | * sorted. | |
83 | * It doesn't affect sorting for static imports. | |
84 | * Type is {@code boolean}. | |
85 | * Default value is {@code true}. | |
86 | * </li> | |
87 | * <li> | |
88 | * Property {@code separated} - control whether type import groups should be separated | |
89 | * by, at least, one blank line or comment and aren't separated internally. | |
90 | * It doesn't affect separations for static imports. | |
91 | * Type is {@code boolean}. | |
92 | * Default value is {@code false}. | |
93 | * </li> | |
94 | * <li> | |
95 | * Property {@code separatedStaticGroups} - control whether static import groups should | |
96 | * be separated by, at least, one blank line or comment and aren't separated internally. | |
97 | * This property has effect only when the property {@code option} is set to {@code top} | |
98 | * or {@code bottom} and when property {@code staticGroups} is enabled. | |
99 | * 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 | * </module> | |
410 | * </pre> | |
411 | * <pre> | |
412 | * import static org.abego.treelayout.Configuration.AlignmentInLevel; // Group 1 | |
413 | * import static java.lang.Math.abs; // Group 2 | |
414 | * import static java.lang.String.format; // Group 2 | |
415 | * import static com.google.common.primitives.Doubles.BYTES; // Group "everything else" | |
416 | * | |
417 | * public class SomeClass { } | |
418 | * </pre> | |
419 | * <p> | |
420 | * The following example shows the idea of 'useContainerOrderingForStatic' option that is | |
421 | * useful for Eclipse IDE users to match ordering validation. | |
422 | * This is how the import comparison works for static imports: we first compare | |
423 | * the container of the static import, container is the type enclosing the static element | |
424 | * being imported. When the result of the comparison is 0 (containers are equal), | |
425 | * we compare the fully qualified import names. | |
426 | * For e.g. this is what is considered to be container names for the given example: | |
427 | * | |
428 | * import static HttpConstants.COLON => HttpConstants | |
429 | * import static HttpHeaders.addHeader => HttpHeaders | |
430 | * import static HttpHeaders.setHeader => HttpHeaders | |
431 | * import static HttpHeaders.Names.DATE => HttpHeaders.Names | |
432 | * | |
433 | * According to this logic, HttpHeaders.Names should come after HttpHeaders. | |
434 | * </p> | |
435 | * <p> | |
436 | * Example for {@code useContainerOrderingForStatic=true} | |
437 | * </p> | |
438 | * <pre> | |
439 | * <module name="ImportOrder"> | |
440 | * <property name="useContainerOrderingForStatic" value="true"/> | |
441 | * <property name="ordered" value="true"/> | |
442 | * <property name="option" value="top"/> | |
443 | * <property name="caseSensitive" value="false"/> | |
444 | * <property name="sortStaticImportsAlphabetically" value="true"/> | |
445 | * </module> | |
446 | * </pre> | |
447 | * <pre> | |
448 | * import static io.netty.handler.codec.http.HttpConstants.COLON; | |
449 | * import static io.netty.handler.codec.http.HttpHeaders.addHeader; | |
450 | * import static io.netty.handler.codec.http.HttpHeaders.setHeader; | |
451 | * import static io.netty.handler.codec.http.HttpHeaders.Names.DATE; | |
452 | * | |
453 | * public class InputEclipseStaticImportsOrder { } | |
454 | * </pre> | |
455 | * <p> | |
456 | * Example for {@code useContainerOrderingForStatic=false} | |
457 | * </p> | |
458 | * <pre> | |
459 | * <module name="ImportOrder"> | |
460 | * <property name="useContainerOrderingForStatic" value="false"/> | |
461 | * <property name="ordered" value="true"/> | |
462 | * <property name="option" value="top"/> | |
463 | * <property name="caseSensitive" value="false"/> | |
464 | * <property name="sortStaticImportsAlphabetically" value="true"/> | |
465 | * </module> | |
466 | * </pre> | |
467 | * <pre> | |
468 | * import static io.netty.handler.codec.http.HttpConstants.COLON; | |
469 | * import static io.netty.handler.codec.http.HttpHeaders.addHeader; | |
470 | * import static io.netty.handler.codec.http.HttpHeaders.setHeader; | |
471 | * import static io.netty.handler.codec.http.HttpHeaders.Names.DATE; // violation | |
472 | * | |
473 | * public class InputEclipseStaticImportsOrder { } | |
474 | * </pre> | |
475 | * <p> | |
476 | * To configure the check to enforce static import group separation | |
477 | * </p> | |
478 | * <p> | |
479 | * Example for {@code separatedStaticGroups=true} | |
480 | * </p> | |
481 | * <pre> | |
482 | * <module name="ImportOrder"> | |
483 | * <property name="staticGroups" value="*,java,javax,org"/> | |
484 | * <property name="option" value="top"/> | |
485 | * <property name="separatedStaticGroups" value="true"/> | |
486 | * </module> | |
487 | * </pre> | |
488 | * <pre> | |
489 | * import static java.lang.Math.PI; | |
490 | * import static java.io.File.createTempFile; | |
491 | * import static javax.swing.JComponent; // violation, should be separated from previous imports | |
492 | * import static javax.WindowConstants.*; // OK | |
493 | * | |
494 | * import java.net.URL; | |
495 | * | |
496 | * public class SomeClass { } | |
497 | * </pre> | |
498 | * <p> | |
499 | * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} | |
500 | * </p> | |
501 | * <p> | |
502 | * Violation Message Keys: | |
503 | * </p> | |
504 | * <ul> | |
505 | * <li> | |
506 | * {@code import.groups.separated.internally} | |
507 | * </li> | |
508 | * <li> | |
509 | * {@code import.ordering} | |
510 | * </li> | |
511 | * <li> | |
512 | * {@code import.separation} | |
513 | * </li> | |
514 | * </ul> | |
515 | * | |
516 | * @since 3.2 | |
517 | */ | |
518 | @FileStatefulCheck | |
519 | public class ImportOrderCheck | |
520 | extends AbstractCheck { | |
521 | ||
522 | /** | |
523 | * A key is pointing to the warning message text in "messages.properties" | |
524 | * file. | |
525 | */ | |
526 | public static final String MSG_SEPARATION = "import.separation"; | |
527 | ||
528 | /** | |
529 | * A key is pointing to the warning message text in "messages.properties" | |
530 | * file. | |
531 | */ | |
532 | public static final String MSG_ORDERING = "import.ordering"; | |
533 | ||
534 | /** | |
535 | * A key is pointing to the warning message text in "messages.properties" | |
536 | * file. | |
537 | */ | |
538 | public static final String MSG_SEPARATED_IN_GROUP = "import.groups.separated.internally"; | |
539 | ||
540 | /** The special wildcard that catches all remaining groups. */ | |
541 | private static final String WILDCARD_GROUP_NAME = "*"; | |
542 | ||
543 | /** Empty array of pattern type needed to initialize check. */ | |
544 | private static final Pattern[] EMPTY_PATTERN_ARRAY = new Pattern[0]; | |
545 | ||
546 | /** | |
547 | * Specify list of <b>type import</b> groups (every group identified either by a common prefix | |
548 | * string, or by a regular expression enclosed in forward slashes (e.g. {@code /regexp/}). | |
549 | * All type imports, which does not match any group, falls into an additional group, | |
550 | * located at the end. Thus, the empty list of type groups (the default value) means one group | |
551 | * for all type imports. | |
552 | */ | |
553 | private Pattern[] groups = EMPTY_PATTERN_ARRAY; | |
554 | ||
555 | /** | |
556 | * Specify list of <b>static</b> import groups (every group identified either by a common prefix | |
557 | * string, or by a regular expression enclosed in forward slashes (e.g. {@code /regexp/}). | |
558 | * All static imports, which does not match any group, falls into an additional group, located | |
559 | * at the end. Thus, the empty list of static groups (the default value) means one group for all | |
560 | * static imports. This property has effect only when the property {@code option} is set to | |
561 | * {@code top} or {@code bottom}. | |
562 | */ | |
563 | private Pattern[] staticGroups = EMPTY_PATTERN_ARRAY; | |
564 | ||
565 | /** | |
566 | * Control whether type import groups should be separated by, at least, one blank | |
567 | * line or comment and aren't separated internally. It doesn't affect separations for static | |
568 | * imports. | |
569 | */ | |
570 | private boolean separated; | |
571 | ||
572 | /** | |
573 | * Control whether static import groups should be separated by, at least, one blank | |
574 | * line or comment and aren't separated internally. This property has effect only when the | |
575 | * property {@code option} is set to {@code top} or {@code bottom} and when property | |
576 | * {@code staticGroups} is enabled. | |
577 | */ | |
578 | private boolean separatedStaticGroups; | |
579 | ||
580 | /** | |
581 | * Control whether type imports within each group should be sorted. | |
582 | * It doesn't affect sorting for static imports. | |
583 | */ | |
584 | private boolean ordered = true; | |
585 | ||
586 | /** | |
587 | * Control whether string comparison should be case sensitive or not. Case sensitive | |
588 | * sorting is in <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>. | |
589 | * It affects both type imports and static imports. | |
590 | */ | |
591 | private boolean caseSensitive = true; | |
592 | ||
593 | /** Last imported group. */ | |
594 | private int lastGroup; | |
595 | /** Line number of last import. */ | |
596 | private int lastImportLine; | |
597 | /** Name of last import. */ | |
598 | private String lastImport; | |
599 | /** If last import was static. */ | |
600 | private boolean lastImportStatic; | |
601 | /** Whether there was any imports. */ | |
602 | private boolean beforeFirstImport; | |
603 | /** | |
604 | * Whether static and type import groups should be split apart. | |
605 | * When the {@code option} property is set to {@code INFLOW}, {@code ABOVE} or {@code UNDER}, | |
606 | * both the type and static imports use the properties {@code groups} and {@code separated}. | |
607 | * When the {@code option} property is set to {@code TOP} or {@code BOTTOM}, static imports | |
608 | * uses the properties {@code staticGroups} and {@code separatedStaticGroups}. | |
609 | **/ | |
610 | private boolean staticImportsApart; | |
611 | ||
612 | /** | |
613 | * Control whether <b>static imports</b> located at <b>top</b> or <b>bottom</b> are | |
614 | * sorted within the group. | |
615 | */ | |
616 | private boolean sortStaticImportsAlphabetically; | |
617 | ||
618 | /** | |
619 | * Control whether to use container ordering (Eclipse IDE term) for static imports | |
620 | * or not. | |
621 | */ | |
622 | private boolean useContainerOrderingForStatic; | |
623 | ||
624 | /** | |
625 | * Specify policy on the relative order between type imports and static imports. | |
626 | */ | |
627 | private ImportOrderOption option = ImportOrderOption.UNDER; | |
628 | ||
629 | /** | |
630 | * Setter to specify policy on the relative order between type imports and static imports. | |
631 | * | |
632 | * @param optionStr string to decode option from | |
633 | * @throws IllegalArgumentException if unable to decode | |
634 | */ | |
635 | public void setOption(String optionStr) { | |
636 | option = ImportOrderOption.valueOf(optionStr.trim().toUpperCase(Locale.ENGLISH)); | |
637 | } | |
638 | ||
639 | /** | |
640 | * Setter to specify list of <b>type import</b> groups (every group identified either by a | |
641 | * common prefix string, or by a regular expression enclosed in forward slashes | |
642 | * (e.g. {@code /regexp/}). All type imports, which does not match any group, falls into an | |
643 | * additional group, located at the end. Thus, the empty list of type groups (the default value) | |
644 | * means one group for all type imports. | |
645 | * | |
646 | * @param packageGroups a comma-separated list of package names/prefixes. | |
647 | */ | |
648 | public void setGroups(String... packageGroups) { | |
649 | groups = compilePatterns(packageGroups); | |
650 | } | |
651 | ||
652 | /** | |
653 | * Setter to specify list of <b>static</b> import groups (every group identified either by a | |
654 | * common prefix string, or by a regular expression enclosed in forward slashes | |
655 | * (e.g. {@code /regexp/}). All static imports, which does not match any group, falls into an | |
656 | * additional group, located at the end. Thus, the empty list of static groups (the default | |
657 | * value) means one group for all static imports. This property has effect only when | |
658 | * the property {@code option} is set to {@code top} or {@code bottom}. | |
659 | * | |
660 | * @param packageGroups a comma-separated list of package names/prefixes. | |
661 | */ | |
662 | public void setStaticGroups(String... packageGroups) { | |
663 | staticGroups = compilePatterns(packageGroups); | |
664 | } | |
665 | ||
666 | /** | |
667 | * Setter to control whether type imports within each group should be sorted. | |
668 | * It doesn't affect sorting for static imports. | |
669 | * | |
670 | * @param ordered | |
671 | * whether lexicographic ordering of imports within a group | |
672 | * required or not. | |
673 | */ | |
674 | public void setOrdered(boolean ordered) { | |
675 | this.ordered = ordered; | |
676 | } | |
677 | ||
678 | /** | |
679 | * Setter to control whether type import groups should be separated by, at least, | |
680 | * one blank line or comment and aren't separated internally. | |
681 | * It doesn't affect separations for static imports. | |
682 | * | |
683 | * @param separated | |
684 | * whether groups should be separated by one blank line or comment. | |
685 | */ | |
686 | public void setSeparated(boolean separated) { | |
687 | this.separated = separated; | |
688 | } | |
689 | ||
690 | /** | |
691 | * Setter to control whether static import groups should be separated by, at least, | |
692 | * one blank line or comment and aren't separated internally. | |
693 | * This property has effect only when the property | |
694 | * {@code option} is set to {@code top} or {@code bottom} and when property {@code staticGroups} | |
695 | * is enabled. | |
696 | * | |
697 | * @param separatedStaticGroups | |
698 | * whether groups should be separated by one blank line or comment. | |
699 | */ | |
700 | public void setSeparatedStaticGroups(boolean separatedStaticGroups) { | |
701 | this.separatedStaticGroups = separatedStaticGroups; | |
702 | } | |
703 | ||
704 | /** | |
705 | * Setter to control whether string comparison should be case sensitive or not. | |
706 | * Case sensitive sorting is in | |
707 | * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a>. | |
708 | * It affects both type imports and static imports. | |
709 | * | |
710 | * @param caseSensitive | |
711 | * whether string comparison should be case sensitive. | |
712 | */ | |
713 | public void setCaseSensitive(boolean caseSensitive) { | |
714 | this.caseSensitive = caseSensitive; | |
715 | } | |
716 | ||
717 | /** | |
718 | * Setter to control whether <b>static imports</b> located at <b>top</b> or | |
719 | * <b>bottom</b> are sorted within the group. | |
720 | * | |
721 | * @param sortAlphabetically true or false. | |
722 | */ | |
723 | public void setSortStaticImportsAlphabetically(boolean sortAlphabetically) { | |
724 | sortStaticImportsAlphabetically = sortAlphabetically; | |
725 | } | |
726 | ||
727 | /** | |
728 | * Setter to control whether to use container ordering (Eclipse IDE term) for static | |
729 | * imports or not. | |
730 | * | |
731 | * @param useContainerOrdering whether to use container ordering for static imports or not. | |
732 | */ | |
733 | public void setUseContainerOrderingForStatic(boolean useContainerOrdering) { | |
734 | useContainerOrderingForStatic = useContainerOrdering; | |
735 | } | |
736 | ||
737 | @Override | |
738 | public int[] getDefaultTokens() { | |
739 |
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(); |
740 | } | |
741 | ||
742 | @Override | |
743 | public int[] getAcceptableTokens() { | |
744 |
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}; |
745 | } | |
746 | ||
747 | @Override | |
748 | public int[] getRequiredTokens() { | |
749 |
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}; |
750 | } | |
751 | ||
752 | @Override | |
753 | public void beginTree(DetailAST rootAST) { | |
754 | lastGroup = Integer.MIN_VALUE; | |
755 | lastImportLine = Integer.MIN_VALUE; | |
756 | lastImport = ""; | |
757 | lastImportStatic = false; | |
758 | beforeFirstImport = true; | |
759 |
6
1. beginTree : negated conditional → KILLED 2. beginTree : negated conditional → KILLED 3. beginTree : removed conditional - replaced equality check with false → KILLED 4. beginTree : removed conditional - replaced equality check with false → KILLED 5. beginTree : removed conditional - replaced equality check with true → KILLED 6. beginTree : removed conditional - replaced equality check with true → KILLED |
staticImportsApart = |
760 | option == ImportOrderOption.TOP || option == ImportOrderOption.BOTTOM; | |
761 | } | |
762 | ||
763 | // -@cs[CyclomaticComplexity] SWITCH was transformed into IF-ELSE. | |
764 | @Override | |
765 | public void visitToken(DetailAST ast) { | |
766 | final FullIdent ident; | |
767 | final boolean isStatic; | |
768 | ||
769 |
3
1. visitToken : negated conditional → KILLED 2. visitToken : removed conditional - replaced equality check with false → KILLED 3. visitToken : removed conditional - replaced equality check with true → KILLED |
if (ast.getType() == TokenTypes.IMPORT) { |
770 | ident = FullIdent.createFullIdentBelow(ast); | |
771 | isStatic = false; | |
772 | } | |
773 | else { | |
774 | ident = FullIdent.createFullIdent(ast.getFirstChild() | |
775 | .getNextSibling()); | |
776 | isStatic = true; | |
777 | } | |
778 | ||
779 | // using set of IF instead of SWITCH to analyze Enum options to satisfy coverage. | |
780 | // https://github.com/checkstyle/checkstyle/issues/1387 | |
781 |
6
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED 3. visitToken : removed conditional - replaced equality check with false → KILLED 4. visitToken : removed conditional - replaced equality check with false → KILLED 5. visitToken : removed conditional - replaced equality check with true → KILLED 6. visitToken : removed conditional - replaced equality check with true → KILLED |
if (option == ImportOrderOption.TOP || option == ImportOrderOption.ABOVE) { |
782 |
6
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED 3. visitToken : removed conditional - replaced equality check with false → KILLED 4. visitToken : removed conditional - replaced equality check with false → KILLED 5. visitToken : removed conditional - replaced equality check with true → KILLED 6. visitToken : removed conditional - replaced equality check with true → KILLED |
final boolean isStaticAndNotLastImport = isStatic && !lastImportStatic; |
783 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitToken → KILLED |
doVisitToken(ident, isStatic, isStaticAndNotLastImport, ast); |
784 | } | |
785 |
6
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED 3. visitToken : removed conditional - replaced equality check with false → KILLED 4. visitToken : removed conditional - replaced equality check with false → KILLED 5. visitToken : removed conditional - replaced equality check with true → KILLED 6. visitToken : removed conditional - replaced equality check with true → KILLED |
else if (option == ImportOrderOption.BOTTOM || option == ImportOrderOption.UNDER) { |
786 |
6
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED 3. visitToken : removed conditional - replaced equality check with false → KILLED 4. visitToken : removed conditional - replaced equality check with false → KILLED 5. visitToken : removed conditional - replaced equality check with true → KILLED 6. visitToken : removed conditional - replaced equality check with true → KILLED |
final boolean isLastImportAndNonStatic = lastImportStatic && !isStatic; |
787 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitToken → KILLED |
doVisitToken(ident, isStatic, isLastImportAndNonStatic, ast); |
788 | } | |
789 |
3
1. visitToken : negated conditional → KILLED 2. visitToken : removed conditional - replaced equality check with false → KILLED 3. visitToken : removed conditional - replaced equality check with true → KILLED |
else if (option == ImportOrderOption.INFLOW) { |
790 | // "previous" argument is useless here | |
791 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitToken → KILLED |
doVisitToken(ident, isStatic, true, ast); |
792 | } | |
793 | else { | |
794 |
2
1. visitToken : removed call to java/lang/StringBuilder::<init> → KILLED 2. visitToken : removed call to java/lang/IllegalStateException::<init> → KILLED |
throw new IllegalStateException( |
795 | "Unexpected option for static imports: " + option); | |
796 | } | |
797 | ||
798 | lastImportLine = ast.findFirstToken(TokenTypes.SEMI).getLineNo(); | |
799 | lastImportStatic = isStatic; | |
800 | beforeFirstImport = false; | |
801 | } | |
802 | ||
803 | /** | |
804 | * Shares processing... | |
805 | * | |
806 | * @param ident the import to process. | |
807 | * @param isStatic whether the token is static or not. | |
808 | * @param previous previous non-static but current is static (above), or | |
809 | * previous static but current is non-static (under). | |
810 | * @param ast node of the AST. | |
811 | */ | |
812 | private void doVisitToken(FullIdent ident, boolean isStatic, boolean previous, DetailAST ast) { | |
813 | final String name = ident.getText(); | |
814 |
6
1. doVisitToken : negated conditional → KILLED 2. doVisitToken : negated conditional → KILLED 3. doVisitToken : removed conditional - replaced equality check with false → KILLED 4. doVisitToken : removed conditional - replaced equality check with false → KILLED 5. doVisitToken : removed conditional - replaced equality check with true → KILLED 6. doVisitToken : removed conditional - replaced equality check with true → KILLED |
final int groupIdx = getGroupNumber(isStatic && staticImportsApart, name); |
815 | ||
816 |
4
1. doVisitToken : changed conditional boundary → KILLED 2. doVisitToken : negated conditional → KILLED 3. doVisitToken : removed conditional - replaced comparison check with false → KILLED 4. doVisitToken : removed conditional - replaced comparison check with true → KILLED |
if (groupIdx > lastGroup) { |
817 |
3
1. doVisitToken : negated conditional → KILLED 2. doVisitToken : removed conditional - replaced equality check with false → KILLED 3. doVisitToken : removed conditional - replaced equality check with true → KILLED |
if (!beforeFirstImport |
818 |
5
1. doVisitToken : changed conditional boundary → KILLED 2. doVisitToken : Replaced integer subtraction with addition → KILLED 3. doVisitToken : negated conditional → KILLED 4. doVisitToken : removed conditional - replaced comparison check with false → KILLED 5. doVisitToken : removed conditional - replaced comparison check with true → KILLED |
&& ast.getLineNo() - lastImportLine < 2 |
819 |
3
1. doVisitToken : negated conditional → KILLED 2. doVisitToken : removed conditional - replaced equality check with false → KILLED 3. doVisitToken : removed conditional - replaced equality check with true → KILLED |
&& needSeparator(isStatic)) { |
820 |
1
1. doVisitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(ast, MSG_SEPARATION, name); |
821 | } | |
822 | } | |
823 |
3
1. doVisitToken : negated conditional → KILLED 2. doVisitToken : removed conditional - replaced equality check with false → KILLED 3. doVisitToken : removed conditional - replaced equality check with true → KILLED |
else if (groupIdx == lastGroup) { |
824 |
1
1. doVisitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::doVisitTokenInSameGroup → KILLED |
doVisitTokenInSameGroup(isStatic, previous, name, ast); |
825 | } | |
826 | else { | |
827 |
1
1. doVisitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(ast, MSG_ORDERING, name); |
828 | } | |
829 |
3
1. doVisitToken : negated conditional → KILLED 2. doVisitToken : removed conditional - replaced equality check with false → KILLED 3. doVisitToken : removed conditional - replaced equality check with true → KILLED |
if (isSeparatorInGroup(groupIdx, isStatic, ast.getLineNo())) { |
830 |
1
1. doVisitToken : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(ast, MSG_SEPARATED_IN_GROUP, name); |
831 | } | |
832 | ||
833 | lastGroup = groupIdx; | |
834 | lastImport = name; | |
835 | } | |
836 | ||
837 | /** | |
838 | * Checks whether import groups should be separated. | |
839 | * | |
840 | * @param isStatic whether the token is static or not. | |
841 | * @return true if imports groups should be separated. | |
842 | */ | |
843 | private boolean needSeparator(boolean isStatic) { | |
844 |
6
1. needSeparator : negated conditional → KILLED 2. needSeparator : negated conditional → KILLED 3. needSeparator : removed conditional - replaced equality check with false → KILLED 4. needSeparator : removed conditional - replaced equality check with false → KILLED 5. needSeparator : removed conditional - replaced equality check with true → KILLED 6. needSeparator : removed conditional - replaced equality check with true → KILLED |
final boolean typeImportSeparator = !isStatic && separated; |
845 | final boolean staticImportSeparator; | |
846 |
3
1. needSeparator : negated conditional → KILLED 2. needSeparator : removed conditional - replaced equality check with false → KILLED 3. needSeparator : removed conditional - replaced equality check with true → KILLED |
if (staticImportsApart) { |
847 |
6
1. needSeparator : negated conditional → KILLED 2. needSeparator : negated conditional → KILLED 3. needSeparator : removed conditional - replaced equality check with false → KILLED 4. needSeparator : removed conditional - replaced equality check with false → KILLED 5. needSeparator : removed conditional - replaced equality check with true → KILLED 6. needSeparator : removed conditional - replaced equality check with true → KILLED |
staticImportSeparator = isStatic && separatedStaticGroups; |
848 | } | |
849 | else { | |
850 | staticImportSeparator = separated; | |
851 | } | |
852 |
9
1. needSeparator : negated conditional → KILLED 2. needSeparator : negated conditional → KILLED 3. needSeparator : negated conditional → KILLED 4. needSeparator : removed conditional - replaced equality check with false → KILLED 5. needSeparator : removed conditional - replaced equality check with false → KILLED 6. needSeparator : removed conditional - replaced equality check with false → KILLED 7. needSeparator : removed conditional - replaced equality check with true → KILLED 8. needSeparator : removed conditional - replaced equality check with true → KILLED 9. needSeparator : removed conditional - replaced equality check with true → KILLED |
final boolean separatorBetween = isStatic != lastImportStatic |
853 | && (separated || separatedStaticGroups); | |
854 | ||
855 |
11
1. needSeparator : replaced boolean return with true for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::needSeparator → KILLED 2. needSeparator : negated conditional → KILLED 3. needSeparator : negated conditional → KILLED 4. needSeparator : negated conditional → KILLED 5. needSeparator : removed conditional - replaced equality check with false → KILLED 6. needSeparator : removed conditional - replaced equality check with false → KILLED 7. needSeparator : removed conditional - replaced equality check with false → KILLED 8. needSeparator : removed conditional - replaced equality check with true → KILLED 9. needSeparator : removed conditional - replaced equality check with true → KILLED 10. needSeparator : removed conditional - replaced equality check with true → KILLED 11. needSeparator : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return typeImportSeparator || staticImportSeparator || separatorBetween; |
856 | } | |
857 | ||
858 | /** | |
859 | * Checks whether imports group separated internally. | |
860 | * | |
861 | * @param groupIdx group number. | |
862 | * @param isStatic whether the token is static or not. | |
863 | * @param line the line of the current import. | |
864 | * @return true if imports group are separated internally. | |
865 | */ | |
866 | private boolean isSeparatorInGroup(int groupIdx, boolean isStatic, int line) { | |
867 |
3
1. isSeparatorInGroup : negated conditional → KILLED 2. isSeparatorInGroup : removed conditional - replaced equality check with false → KILLED 3. isSeparatorInGroup : removed conditional - replaced equality check with true → KILLED |
final boolean inSameGroup = groupIdx == lastGroup; |
868 |
11
1. isSeparatorInGroup : replaced boolean return with true for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::isSeparatorInGroup → KILLED 2. isSeparatorInGroup : negated conditional → KILLED 3. isSeparatorInGroup : negated conditional → KILLED 4. isSeparatorInGroup : negated conditional → KILLED 5. isSeparatorInGroup : removed conditional - replaced equality check with false → KILLED 6. isSeparatorInGroup : removed conditional - replaced equality check with false → KILLED 7. isSeparatorInGroup : removed conditional - replaced equality check with false → KILLED 8. isSeparatorInGroup : removed conditional - replaced equality check with true → KILLED 9. isSeparatorInGroup : removed conditional - replaced equality check with true → KILLED 10. isSeparatorInGroup : removed conditional - replaced equality check with true → KILLED 11. isSeparatorInGroup : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return (inSameGroup || !needSeparator(isStatic)) && isSeparatorBeforeImport(line); |
869 | } | |
870 | ||
871 | /** | |
872 | * Checks whether there is any separator before current import. | |
873 | * | |
874 | * @param line the line of the current import. | |
875 | * @return true if there is separator before current import which isn't the first import. | |
876 | */ | |
877 | private boolean isSeparatorBeforeImport(int line) { | |
878 |
7
1. isSeparatorBeforeImport : replaced boolean return with true for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::isSeparatorBeforeImport → KILLED 2. isSeparatorBeforeImport : changed conditional boundary → KILLED 3. isSeparatorBeforeImport : Replaced integer subtraction with addition → KILLED 4. isSeparatorBeforeImport : negated conditional → KILLED 5. isSeparatorBeforeImport : removed conditional - replaced comparison check with false → KILLED 6. isSeparatorBeforeImport : removed conditional - replaced comparison check with true → KILLED 7. isSeparatorBeforeImport : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return line - lastImportLine > 1; |
879 | } | |
880 | ||
881 | /** | |
882 | * Shares processing... | |
883 | * | |
884 | * @param isStatic whether the token is static or not. | |
885 | * @param previous previous non-static but current is static (above), or | |
886 | * previous static but current is non-static (under). | |
887 | * @param name the name of the current import. | |
888 | * @param ast node of the AST. | |
889 | */ | |
890 | private void doVisitTokenInSameGroup(boolean isStatic, | |
891 | boolean previous, String name, DetailAST ast) { | |
892 |
3
1. doVisitTokenInSameGroup : negated conditional → KILLED 2. doVisitTokenInSameGroup : removed conditional - replaced equality check with false → KILLED 3. doVisitTokenInSameGroup : removed conditional - replaced equality check with true → KILLED |
if (ordered) { |
893 |
3
1. doVisitTokenInSameGroup : negated conditional → KILLED 2. doVisitTokenInSameGroup : removed conditional - replaced equality check with false → KILLED 3. doVisitTokenInSameGroup : removed conditional - replaced equality check with true → KILLED |
if (option == ImportOrderOption.INFLOW) { |
894 |
3
1. doVisitTokenInSameGroup : negated conditional → KILLED 2. doVisitTokenInSameGroup : removed conditional - replaced equality check with false → KILLED 3. doVisitTokenInSameGroup : removed conditional - replaced equality check with true → KILLED |
if (isWrongOrder(name, isStatic)) { |
895 |
1
1. doVisitTokenInSameGroup : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(ast, MSG_ORDERING, name); |
896 | } | |
897 | } | |
898 | else { | |
899 |
6
1. doVisitTokenInSameGroup : negated conditional → KILLED 2. doVisitTokenInSameGroup : negated conditional → KILLED 3. doVisitTokenInSameGroup : removed conditional - replaced equality check with false → KILLED 4. doVisitTokenInSameGroup : removed conditional - replaced equality check with false → KILLED 5. doVisitTokenInSameGroup : removed conditional - replaced equality check with true → KILLED 6. doVisitTokenInSameGroup : removed conditional - replaced equality check with true → KILLED |
final boolean shouldFireError = |
900 | // previous non-static but current is static (above) | |
901 | // or | |
902 | // previous static but current is non-static (under) | |
903 | previous | |
904 | || | |
905 | // current and previous static or current and | |
906 | // previous non-static | |
907 | lastImportStatic == isStatic | |
908 |
3
1. doVisitTokenInSameGroup : negated conditional → KILLED 2. doVisitTokenInSameGroup : removed conditional - replaced equality check with false → KILLED 3. doVisitTokenInSameGroup : removed conditional - replaced equality check with true → KILLED |
&& isWrongOrder(name, isStatic); |
909 | ||
910 |
3
1. doVisitTokenInSameGroup : negated conditional → KILLED 2. doVisitTokenInSameGroup : removed conditional - replaced equality check with false → KILLED 3. doVisitTokenInSameGroup : removed conditional - replaced equality check with true → KILLED |
if (shouldFireError) { |
911 |
1
1. doVisitTokenInSameGroup : removed call to com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::log → KILLED |
log(ast, MSG_ORDERING, name); |
912 | } | |
913 | } | |
914 | } | |
915 | } | |
916 | ||
917 | /** | |
918 | * Checks whether import name is in wrong order. | |
919 | * | |
920 | * @param name import name. | |
921 | * @param isStatic whether it is a static import name. | |
922 | * @return true if import name is in wrong order. | |
923 | */ | |
924 | private boolean isWrongOrder(String name, boolean isStatic) { | |
925 | final boolean result; | |
926 |
3
1. isWrongOrder : negated conditional → KILLED 2. isWrongOrder : removed conditional - replaced equality check with false → KILLED 3. isWrongOrder : removed conditional - replaced equality check with true → KILLED |
if (isStatic) { |
927 |
3
1. isWrongOrder : negated conditional → KILLED 2. isWrongOrder : removed conditional - replaced equality check with false → KILLED 3. isWrongOrder : removed conditional - replaced equality check with true → KILLED |
if (useContainerOrderingForStatic) { |
928 |
4
1. isWrongOrder : changed conditional boundary → KILLED 2. isWrongOrder : negated conditional → KILLED 3. isWrongOrder : removed conditional - replaced comparison check with false → KILLED 4. isWrongOrder : removed conditional - replaced comparison check with true → KILLED |
result = compareContainerOrder(lastImport, name, caseSensitive) > 0; |
929 | } | |
930 |
3
1. isWrongOrder : negated conditional → KILLED 2. isWrongOrder : removed conditional - replaced equality check with false → KILLED 3. isWrongOrder : removed conditional - replaced equality check with true → KILLED |
else if (staticImportsApart) { |
931 |
3
1. isWrongOrder : negated conditional → KILLED 2. isWrongOrder : removed conditional - replaced equality check with false → KILLED 3. isWrongOrder : removed conditional - replaced equality check with true → KILLED |
result = sortStaticImportsAlphabetically |
932 |
4
1. isWrongOrder : changed conditional boundary → KILLED 2. isWrongOrder : negated conditional → KILLED 3. isWrongOrder : removed conditional - replaced comparison check with false → KILLED 4. isWrongOrder : removed conditional - replaced comparison check with true → KILLED |
&& compare(lastImport, name, caseSensitive) > 0; |
933 | } | |
934 | else { | |
935 |
4
1. isWrongOrder : changed conditional boundary → KILLED 2. isWrongOrder : negated conditional → KILLED 3. isWrongOrder : removed conditional - replaced comparison check with false → KILLED 4. isWrongOrder : removed conditional - replaced comparison check with true → KILLED |
result = compare(lastImport, name, caseSensitive) > 0; |
936 | } | |
937 | } | |
938 | else { | |
939 | // out of lexicographic order | |
940 |
4
1. isWrongOrder : changed conditional boundary → KILLED 2. isWrongOrder : negated conditional → KILLED 3. isWrongOrder : removed conditional - replaced comparison check with false → KILLED 4. isWrongOrder : removed conditional - replaced comparison check with true → KILLED |
result = compare(lastImport, name, caseSensitive) > 0; |
941 | } | |
942 |
3
1. isWrongOrder : replaced boolean return with false for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::isWrongOrder → KILLED 2. isWrongOrder : replaced boolean return with true for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::isWrongOrder → KILLED 3. isWrongOrder : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
943 | } | |
944 | ||
945 | /** | |
946 | * Compares two import strings. | |
947 | * We first compare the container of the static import, container being the type enclosing | |
948 | * the static element being imported. When this returns 0, we compare the qualified | |
949 | * import name. For e.g. this is what is considered to be container names: | |
950 | * <p> | |
951 | * import static HttpConstants.COLON => HttpConstants | |
952 | * import static HttpHeaders.addHeader => HttpHeaders | |
953 | * import static HttpHeaders.setHeader => HttpHeaders | |
954 | * import static HttpHeaders.Names.DATE => HttpHeaders.Names | |
955 | * </p> | |
956 | * <p> | |
957 | * According to this logic, HttpHeaders.Names would come after HttpHeaders. | |
958 | * | |
959 | * For more details, see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=473629#c3"> | |
960 | * static imports comparison method</a> in Eclipse. | |
961 | * </p> | |
962 | * | |
963 | * @param importName1 first import name. | |
964 | * @param importName2 second import name. | |
965 | * @param caseSensitive whether the comparison of fully qualified import names is case | |
966 | * sensitive. | |
967 | * @return the value {@code 0} if str1 is equal to str2; a value | |
968 | * less than {@code 0} if str is less than the str2 (container order | |
969 | * or lexicographical); and a value greater than {@code 0} if str1 is greater than str2 | |
970 | * (container order or lexicographically). | |
971 | */ | |
972 | private static int compareContainerOrder(String importName1, String importName2, | |
973 | boolean caseSensitive) { | |
974 | final String container1 = getImportContainer(importName1); | |
975 | final String container2 = getImportContainer(importName2); | |
976 | final int compareContainersOrderResult; | |
977 |
3
1. compareContainerOrder : negated conditional → KILLED 2. compareContainerOrder : removed conditional - replaced equality check with false → KILLED 3. compareContainerOrder : removed conditional - replaced equality check with true → KILLED |
if (caseSensitive) { |
978 | compareContainersOrderResult = container1.compareTo(container2); | |
979 | } | |
980 | else { | |
981 | compareContainersOrderResult = container1.compareToIgnoreCase(container2); | |
982 | } | |
983 | final int result; | |
984 |
3
1. compareContainerOrder : negated conditional → KILLED 2. compareContainerOrder : removed conditional - replaced equality check with false → KILLED 3. compareContainerOrder : removed conditional - replaced equality check with true → KILLED |
if (compareContainersOrderResult == 0) { |
985 | result = compare(importName1, importName2, caseSensitive); | |
986 | } | |
987 | else { | |
988 | result = compareContainersOrderResult; | |
989 | } | |
990 |
1
1. compareContainerOrder : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
991 | } | |
992 | ||
993 | /** | |
994 | * Extracts import container name from fully qualified import name. | |
995 | * An import container name is the type which encloses the static element being imported. | |
996 | * For example, HttpConstants, HttpHeaders, HttpHeaders.Names are import container names: | |
997 | * <p> | |
998 | * import static HttpConstants.COLON => HttpConstants | |
999 | * import static HttpHeaders.addHeader => HttpHeaders | |
1000 | * import static HttpHeaders.setHeader => HttpHeaders | |
1001 | * import static HttpHeaders.Names.DATE => HttpHeaders.Names | |
1002 | * </p> | |
1003 | * | |
1004 | * @param qualifiedImportName fully qualified import name. | |
1005 | * @return import container name. | |
1006 | */ | |
1007 | private static String getImportContainer(String qualifiedImportName) { | |
1008 | final int lastDotIndex = qualifiedImportName.lastIndexOf('.'); | |
1009 |
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); |
1010 | } | |
1011 | ||
1012 | /** | |
1013 | * Finds out what group the specified import belongs to. | |
1014 | * | |
1015 | * @param isStatic whether the token is static or not. | |
1016 | * @param name the import name to find. | |
1017 | * @return group number for given import name. | |
1018 | */ | |
1019 | private int getGroupNumber(boolean isStatic, String name) { | |
1020 | final Pattern[] patterns; | |
1021 |
3
1. getGroupNumber : negated conditional → KILLED 2. getGroupNumber : removed conditional - replaced equality check with false → KILLED 3. getGroupNumber : removed conditional - replaced equality check with true → KILLED |
if (isStatic) { |
1022 | patterns = staticGroups; | |
1023 | } | |
1024 | else { | |
1025 | patterns = groups; | |
1026 | } | |
1027 | ||
1028 | int number = getGroupNumber(patterns, name); | |
1029 | ||
1030 |
6
1. getGroupNumber : negated conditional → KILLED 2. getGroupNumber : negated conditional → KILLED 3. getGroupNumber : removed conditional - replaced equality check with false → KILLED 4. getGroupNumber : removed conditional - replaced equality check with false → KILLED 5. getGroupNumber : removed conditional - replaced equality check with true → KILLED 6. getGroupNumber : removed conditional - replaced equality check with true → KILLED |
if (isStatic && option == ImportOrderOption.BOTTOM) { |
1031 |
2
1. getGroupNumber : Replaced integer addition with subtraction → KILLED 2. getGroupNumber : Replaced integer addition with subtraction → KILLED |
number += groups.length + 1; |
1032 | } | |
1033 |
6
1. getGroupNumber : negated conditional → KILLED 2. getGroupNumber : negated conditional → KILLED 3. getGroupNumber : removed conditional - replaced equality check with false → KILLED 4. getGroupNumber : removed conditional - replaced equality check with false → KILLED 5. getGroupNumber : removed conditional - replaced equality check with true → KILLED 6. getGroupNumber : removed conditional - replaced equality check with true → KILLED |
else if (!isStatic && option == ImportOrderOption.TOP) { |
1034 |
2
1. getGroupNumber : Replaced integer addition with subtraction → KILLED 2. getGroupNumber : Replaced integer addition with subtraction → KILLED |
number += staticGroups.length + 1; |
1035 | } | |
1036 |
1
1. getGroupNumber : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return number; |
1037 | } | |
1038 | ||
1039 | /** | |
1040 | * Finds out what group the specified import belongs to. | |
1041 | * | |
1042 | * @param patterns groups to check. | |
1043 | * @param name the import name to find. | |
1044 | * @return group number for given import name. | |
1045 | */ | |
1046 | private static int getGroupNumber(Pattern[] patterns, String name) { | |
1047 | int bestIndex = patterns.length; | |
1048 | int bestEnd = -1; | |
1049 | int bestPos = Integer.MAX_VALUE; | |
1050 | ||
1051 | // find out what group this belongs in | |
1052 | // loop over patterns and get index | |
1053 |
5
1. getGroupNumber : changed conditional boundary → KILLED 2. getGroupNumber : Changed increment from 1 to -1 → KILLED 3. getGroupNumber : negated conditional → KILLED 4. getGroupNumber : removed conditional - replaced comparison check with false → KILLED 5. getGroupNumber : removed conditional - replaced comparison check with true → KILLED |
for (int i = 0; i < patterns.length; i++) { |
1054 | final Matcher matcher = patterns[i].matcher(name); | |
1055 |
3
1. getGroupNumber : negated conditional → KILLED 2. getGroupNumber : removed conditional - replaced equality check with false → KILLED 3. getGroupNumber : removed conditional - replaced equality check with true → KILLED |
if (matcher.find()) { |
1056 |
4
1. getGroupNumber : changed conditional boundary → KILLED 2. getGroupNumber : negated conditional → KILLED 3. getGroupNumber : removed conditional - replaced comparison check with false → KILLED 4. getGroupNumber : removed conditional - replaced comparison check with true → KILLED |
if (matcher.start() < bestPos) { |
1057 | bestIndex = i; | |
1058 | bestEnd = matcher.end(); | |
1059 | bestPos = matcher.start(); | |
1060 | } | |
1061 |
7
1. getGroupNumber : changed conditional boundary → KILLED 2. getGroupNumber : negated conditional → KILLED 3. getGroupNumber : negated conditional → KILLED 4. getGroupNumber : removed conditional - replaced equality check with false → KILLED 5. getGroupNumber : removed conditional - replaced equality check with true → KILLED 6. getGroupNumber : removed conditional - replaced comparison check with false → KILLED 7. getGroupNumber : removed conditional - replaced comparison check with true → KILLED |
else if (matcher.start() == bestPos && matcher.end() > bestEnd) { |
1062 | bestIndex = i; | |
1063 | bestEnd = matcher.end(); | |
1064 | } | |
1065 | } | |
1066 | } | |
1067 |
1
1. getGroupNumber : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return bestIndex; |
1068 | } | |
1069 | ||
1070 | /** | |
1071 | * Compares two strings. | |
1072 | * | |
1073 | * @param string1 | |
1074 | * the first string. | |
1075 | * @param string2 | |
1076 | * the second string. | |
1077 | * @param caseSensitive | |
1078 | * whether the comparison is case sensitive. | |
1079 | * @return the value {@code 0} if string1 is equal to string2; a value | |
1080 | * less than {@code 0} if string1 is lexicographically less | |
1081 | * than the string2; and a value greater than {@code 0} if | |
1082 | * string1 is lexicographically greater than string2. | |
1083 | */ | |
1084 | private static int compare(String string1, String string2, | |
1085 | boolean caseSensitive) { | |
1086 | final int result; | |
1087 |
3
1. compare : negated conditional → KILLED 2. compare : removed conditional - replaced equality check with false → KILLED 3. compare : removed conditional - replaced equality check with true → KILLED |
if (caseSensitive) { |
1088 | result = string1.compareTo(string2); | |
1089 | } | |
1090 | else { | |
1091 | result = string1.compareToIgnoreCase(string2); | |
1092 | } | |
1093 | ||
1094 |
1
1. compare : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
1095 | } | |
1096 | ||
1097 | /** | |
1098 | * Compiles the list of package groups and the order they should occur in the file. | |
1099 | * | |
1100 | * @param packageGroups a comma-separated list of package names/prefixes. | |
1101 | * @return array of compiled patterns. | |
1102 | */ | |
1103 | private static Pattern[] compilePatterns(String... packageGroups) { | |
1104 | final Pattern[] patterns = new Pattern[packageGroups.length]; | |
1105 | ||
1106 |
4
1. compilePatterns : changed conditional boundary → KILLED 2. compilePatterns : negated conditional → KILLED 3. compilePatterns : removed conditional - replaced comparison check with false → KILLED 4. compilePatterns : removed conditional - replaced comparison check with true → KILLED |
for (int i = 0; i < packageGroups.length; i++) { |
1107 | String pkg = packageGroups[i]; | |
1108 | final Pattern grp; | |
1109 | ||
1110 | // if the pkg name is the wildcard, make it match zero chars | |
1111 | // from any name, so it will always be used as last resort. | |
1112 |
3
1. compilePatterns : negated conditional → KILLED 2. compilePatterns : removed conditional - replaced equality check with false → KILLED 3. compilePatterns : removed conditional - replaced equality check with true → KILLED |
if (WILDCARD_GROUP_NAME.equals(pkg)) { |
1113 | // matches any package | |
1114 | grp = Pattern.compile(""); | |
1115 | } | |
1116 |
3
1. compilePatterns : negated conditional → KILLED 2. compilePatterns : removed conditional - replaced equality check with false → KILLED 3. compilePatterns : removed conditional - replaced equality check with true → KILLED |
else if (CommonUtil.startsWithChar(pkg, '/')) { |
1117 |
3
1. compilePatterns : negated conditional → KILLED 2. compilePatterns : removed conditional - replaced equality check with false → KILLED 3. compilePatterns : removed conditional - replaced equality check with true → KILLED |
if (!CommonUtil.endsWithChar(pkg, '/')) { |
1118 |
2
1. compilePatterns : removed call to java/lang/StringBuilder::<init> → KILLED 2. compilePatterns : removed call to java/lang/IllegalArgumentException::<init> → KILLED |
throw new IllegalArgumentException("Invalid group: " + pkg); |
1119 | } | |
1120 |
1
1. compilePatterns : Replaced integer subtraction with addition → KILLED |
pkg = pkg.substring(1, pkg.length() - 1); |
1121 | grp = Pattern.compile(pkg); | |
1122 | } | |
1123 | else { | |
1124 |
1
1. compilePatterns : removed call to java/lang/StringBuilder::<init> → KILLED |
final StringBuilder pkgBuilder = new StringBuilder(pkg); |
1125 |
3
1. compilePatterns : negated conditional → KILLED 2. compilePatterns : removed conditional - replaced equality check with false → KILLED 3. compilePatterns : removed conditional - replaced equality check with true → KILLED |
if (!CommonUtil.endsWithChar(pkg, '.')) { |
1126 | pkgBuilder.append('.'); | |
1127 | } | |
1128 |
1
1. compilePatterns : removed call to java/lang/StringBuilder::<init> → KILLED |
grp = Pattern.compile("^" + Pattern.quote(pkgBuilder.toString())); |
1129 | } | |
1130 | ||
1131 | patterns[i] = grp; | |
1132 | } | |
1133 |
1
1. compilePatterns : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/ImportOrderCheck::compilePatterns to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return patterns; |
1134 | } | |
1135 | ||
1136 | } | |
Mutations | ||
739 |
1.1 |
|
744 |
1.1 |
|
749 |
1.1 |
|
759 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
769 |
1.1 2.2 3.3 |
|
781 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
782 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
783 |
1.1 |
|
785 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
786 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
787 |
1.1 |
|
789 |
1.1 2.2 3.3 |
|
791 |
1.1 |
|
794 |
1.1 2.2 |
|
814 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
816 |
1.1 2.2 3.3 4.4 |
|
817 |
1.1 2.2 3.3 |
|
818 |
1.1 2.2 3.3 4.4 5.5 |
|
819 |
1.1 2.2 3.3 |
|
820 |
1.1 |
|
823 |
1.1 2.2 3.3 |
|
824 |
1.1 |
|
827 |
1.1 |
|
829 |
1.1 2.2 3.3 |
|
830 |
1.1 |
|
844 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
846 |
1.1 2.2 3.3 |
|
847 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
852 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 |
|
855 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 10.10 11.11 |
|
867 |
1.1 2.2 3.3 |
|
868 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 10.10 11.11 |
|
878 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 |
|
892 |
1.1 2.2 3.3 |
|
893 |
1.1 2.2 3.3 |
|
894 |
1.1 2.2 3.3 |
|
895 |
1.1 |
|
899 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
908 |
1.1 2.2 3.3 |
|
910 |
1.1 2.2 3.3 |
|
911 |
1.1 |
|
926 |
1.1 2.2 3.3 |
|
927 |
1.1 2.2 3.3 |
|
928 |
1.1 2.2 3.3 4.4 |
|
930 |
1.1 2.2 3.3 |
|
931 |
1.1 2.2 3.3 |
|
932 |
1.1 2.2 3.3 4.4 |
|
935 |
1.1 2.2 3.3 4.4 |
|
940 |
1.1 2.2 3.3 4.4 |
|
942 |
1.1 2.2 3.3 |
|
977 |
1.1 2.2 3.3 |
|
984 |
1.1 2.2 3.3 |
|
990 |
1.1 |
|
1009 |
1.1 |
|
1021 |
1.1 2.2 3.3 |
|
1030 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
1031 |
1.1 2.2 |
|
1033 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
1034 |
1.1 2.2 |
|
1036 |
1.1 |
|
1053 |
1.1 2.2 3.3 4.4 5.5 |
|
1055 |
1.1 2.2 3.3 |
|
1056 |
1.1 2.2 3.3 4.4 |
|
1061 |
1.1 2.2 3.3 4.4 5.5 6.6 7.7 |
|
1067 |
1.1 |
|
1087 |
1.1 2.2 3.3 |
|
1094 |
1.1 |
|
1106 |
1.1 2.2 3.3 4.4 |
|
1112 |
1.1 2.2 3.3 |
|
1116 |
1.1 2.2 3.3 |
|
1117 |
1.1 2.2 3.3 |
|
1118 |
1.1 2.2 |
|
1120 |
1.1 |
|
1124 |
1.1 |
|
1125 |
1.1 2.2 3.3 |
|
1128 |
1.1 |
|
1133 |
1.1 |