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.ArrayList; | |
23 | import java.util.List; | |
24 | import java.util.StringTokenizer; | |
25 | import java.util.regex.Matcher; | |
26 | import java.util.regex.Pattern; | |
27 | ||
28 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
29 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
30 | import com.puppycrawl.tools.checkstyle.api.FullIdent; | |
31 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
32 | import com.puppycrawl.tools.checkstyle.utils.CommonUtils; | |
33 | ||
34 | /** | |
35 | * <p> | |
36 | * Checks that the groups of import declarations appear in the order specified | |
37 | * by the user. If there is an import but its group is not specified in the | |
38 | * configuration such an import should be placed at the end of the import list. | |
39 | * </p> | |
40 | * The rule consists of: | |
41 | * | |
42 | * <p> | |
43 | * 1. STATIC group. This group sets the ordering of static imports. | |
44 | * </p> | |
45 | * | |
46 | * <p> | |
47 | * 2. SAME_PACKAGE(n) group. This group sets the ordering of the same package imports. | |
48 | * Imports are considered on SAME_PACKAGE group if <b>n</b> first domains in package name | |
49 | * and import name are identical. | |
50 | * </p> | |
51 | * | |
52 | * <pre> | |
53 | *{@code | |
54 | *package java.util.concurrent.locks; | |
55 | * | |
56 | *import java.io.File; | |
57 | *import java.util.*; //#1 | |
58 | *import java.util.List; //#2 | |
59 | *import java.util.StringTokenizer; //#3 | |
60 | *import java.util.concurrent.*; //#4 | |
61 | *import java.util.concurrent.AbstractExecutorService; //#5 | |
62 | *import java.util.concurrent.locks.LockSupport; //#6 | |
63 | *import java.util.regex.Pattern; //#7 | |
64 | *import java.util.regex.Matcher; //#8 | |
65 | *} | |
66 | * </pre> | |
67 | * | |
68 | * <p> | |
69 | * If we have SAME_PACKAGE(3) on configuration file, | |
70 | * imports #4-6 will be considered as a SAME_PACKAGE group (java.util.concurrent.*, | |
71 | * java.util.concurrent.AbstractExecutorService, java.util.concurrent.locks.LockSupport). | |
72 | * SAME_PACKAGE(2) will include #1-8. SAME_PACKAGE(4) will include only #6. | |
73 | * SAME_PACKAGE(5) will result in no imports assigned to SAME_PACKAGE group because | |
74 | * actual package java.util.concurrent.locks has only 4 domains. | |
75 | * </p> | |
76 | * | |
77 | * <p> | |
78 | * 3. THIRD_PARTY_PACKAGE group. This group sets ordering of third party imports. | |
79 | * Third party imports are all imports except STATIC, | |
80 | * SAME_PACKAGE(n), STANDARD_JAVA_PACKAGE and SPECIAL_IMPORTS. | |
81 | * </p> | |
82 | * | |
83 | * <p> | |
84 | * 4. STANDARD_JAVA_PACKAGE group. By default this group sets ordering of standard java/javax | |
85 | * imports. | |
86 | * </p> | |
87 | * | |
88 | * <p> | |
89 | * 5. SPECIAL_IMPORTS group. This group may contains some imports | |
90 | * that have particular meaning for the user. | |
91 | * </p> | |
92 | * | |
93 | * <p> | |
94 | * NOTE! | |
95 | * </p> | |
96 | * <p> | |
97 | * Use the separator '###' between rules. | |
98 | * </p> | |
99 | * <p> | |
100 | * To set RegExps for THIRD_PARTY_PACKAGE and STANDARD_JAVA_PACKAGE groups use | |
101 | * thirdPartyPackageRegExp and standardPackageRegExp options. | |
102 | * </p> | |
103 | * <p> | |
104 | * Pretty often one import can match more than one group. For example, static import from standard | |
105 | * package or regular expressions are configured to allow one import match multiple groups. | |
106 | * In this case, group will be assigned according to priorities: | |
107 | * </p> | |
108 | * <ol> | |
109 | * <li> | |
110 | * STATIC has top priority | |
111 | * </li> | |
112 | * <li> | |
113 | * SAME_PACKAGE has second priority | |
114 | * </li> | |
115 | * <li> | |
116 | * STANDARD_JAVA_PACKAGE and SPECIAL_IMPORTS will compete using "best match" rule: longer | |
117 | * matching substring wins; in case of the same length, lower position of matching substring | |
118 | * wins; if position is the same, order of rules in configuration solves the puzzle. | |
119 | * </li> | |
120 | * <li> | |
121 | * THIRD_PARTY has the least priority | |
122 | * </li> | |
123 | * </ol> | |
124 | * <p> | |
125 | * Few examples to illustrate "best match": | |
126 | * </p> | |
127 | * <p> | |
128 | * 1. patterns STANDARD_JAVA_PACKAGE = "Check", SPECIAL_IMPORTS="ImportOrderCheck" and input | |
129 | * file: | |
130 | * </p> | |
131 | * <pre> | |
132 | *{@code | |
133 | *import com.puppycrawl.tools.checkstyle.checks.imports.CustomImportOrderCheck; | |
134 | *import com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck;} | |
135 | * </pre> | |
136 | * <p> | |
137 | * Result: imports will be assigned to SPECIAL_IMPORTS, because matching substring length is 16. | |
138 | * Matching substring for STANDARD_JAVA_PACKAGE is 5. | |
139 | * </p> | |
140 | * <p> | |
141 | * 2. patterns STANDARD_JAVA_PACKAGE = "Check", SPECIAL_IMPORTS="Avoid" and file: | |
142 | * </p> | |
143 | * <pre> | |
144 | *{@code | |
145 | *import com.puppycrawl.tools.checkstyle.checks.imports.AvoidStarImportCheck;} | |
146 | * </pre> | |
147 | * <p> | |
148 | * Result: import will be assigned to SPECIAL_IMPORTS. Matching substring length is 5 for both | |
149 | * patterns. However, "Avoid" position is lower then "Check" position. | |
150 | * </p> | |
151 | * | |
152 | * <pre> | |
153 | * Properties: | |
154 | * </pre> | |
155 | * <table summary="Properties" border="1"> | |
156 | * <tr><th>name</th><th>Description</th><th>type</th><th>default value</th></tr> | |
157 | * <tr><td>customImportOrderRules</td><td>List of order declaration customizing by user.</td> | |
158 | * <td>string</td><td>null</td></tr> | |
159 | * <tr><td>standardPackageRegExp</td><td>RegExp for STANDARD_JAVA_PACKAGE group imports.</td> | |
160 | * <td>regular expression</td><td>^(java|javax)\.</td></tr> | |
161 | * <tr><td>thirdPartyPackageRegExp</td><td>RegExp for THIRD_PARTY_PACKAGE group imports.</td> | |
162 | * <td>regular expression</td><td>.*</td></tr> | |
163 | * <tr><td>specialImportsRegExp</td><td>RegExp for SPECIAL_IMPORTS group imports.</td> | |
164 | * <td>regular expression</td><td>^$</td></tr> | |
165 | * <tr><td>separateLineBetweenGroups</td><td>Force empty line separator between import groups. | |
166 | * </td><td>boolean</td><td>true</td></tr> | |
167 | * <tr><td>sortImportsInGroupAlphabetically</td><td>Force grouping alphabetically, | |
168 | * in ASCII sort order.</td><td>boolean</td><td>false</td></tr> | |
169 | * </table> | |
170 | * | |
171 | * <p> | |
172 | * For example: | |
173 | * </p> | |
174 | * <p>To configure the check so that it matches default Eclipse formatter configuration | |
175 | * (tested on Kepler, Luna and Mars):</p> | |
176 | * <ul> | |
177 | * <li>group of static imports is on the top</li> | |
178 | * <li>groups of non-static imports: "java" and "javax" packages | |
179 | * first, then "org" and then all other imports</li> | |
180 | * <li>imports will be sorted in the groups</li> | |
181 | * <li>groups are separated by, at least, one blank line</li> | |
182 | * </ul> | |
183 | * <pre> | |
184 | * <module name="CustomImportOrder"> | |
185 | * <property name="customImportOrderRules" | |
186 | * value="STATIC###STANDARD_JAVA_PACKAGE###SPECIAL_IMPORTS"/> | |
187 | * <property name="specialImportsRegExp" value="org"/> | |
188 | * <property name="sortImportsInGroupAlphabetically" value="true"/> | |
189 | * <property name="separateLineBetweenGroups" value="true"/> | |
190 | * </module> | |
191 | * </pre> | |
192 | * | |
193 | * <p>To configure the check so that it matches default IntelliJ IDEA formatter | |
194 | * configuration (tested on v14):</p> | |
195 | * <ul> | |
196 | * <li>group of static imports is on the bottom</li> | |
197 | * <li>groups of non-static imports: all imports except of "javax" | |
198 | * and "java", then "javax" and "java"</li> | |
199 | * <li>imports will be sorted in the groups</li> | |
200 | * <li>groups are separated by, at least, one blank line</li> | |
201 | * </ul> | |
202 | * | |
203 | * <p> | |
204 | * Note: "separated" option is disabled because IDEA default has blank line | |
205 | * between "java" and static imports, and no blank line between | |
206 | * "javax" and "java" | |
207 | * </p> | |
208 | * | |
209 | * <pre> | |
210 | * <module name="CustomImportOrder"> | |
211 | * <property name="customImportOrderRules" | |
212 | * value="THIRD_PARTY_PACKAGE###SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE | |
213 | * ###STATIC"/> | |
214 | * <property name="specialImportsRegExp" value="^javax\."/> | |
215 | * <property name="standardPackageRegExp" value="^java\."/> | |
216 | * <property name="sortImportsInGroupAlphabetically" value="true"/> | |
217 | * <property name="separateLineBetweenGroups" value="false"/> | |
218 | *</module> | |
219 | * </pre> | |
220 | * | |
221 | * <p>To configure the check so that it matches default NetBeans formatter | |
222 | * configuration (tested on v8):</p> | |
223 | * <ul> | |
224 | * <li>groups of non-static imports are not defined, all imports will be sorted as a one | |
225 | * group</li> | |
226 | * <li>static imports are not separated, they will be sorted along with other imports</li> | |
227 | * </ul> | |
228 | * | |
229 | * <pre> | |
230 | *<module name="CustomImportOrder"/> | |
231 | * </pre> | |
232 | * <p>To set RegExps for THIRD_PARTY_PACKAGE and STANDARD_JAVA_PACKAGE groups use | |
233 | * thirdPartyPackageRegExp and standardPackageRegExp options.</p> | |
234 | * <pre> | |
235 | * <module name="CustomImportOrder"> | |
236 | * <property name="customImportOrderRules" | |
237 | * value="STATIC###SAME_PACKAGE(3)###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE"/> | |
238 | * <property name="thirdPartyPackageRegExp" value="com|org"/> | |
239 | * <property name="standardPackageRegExp" value="^(java|javax)\."/> | |
240 | * </module> | |
241 | * </pre> | |
242 | * <p> | |
243 | * Also, this check can be configured to force empty line separator between | |
244 | * import groups. For example | |
245 | * </p> | |
246 | * | |
247 | * <pre> | |
248 | * <module name="CustomImportOrder"> | |
249 | * <property name="separateLineBetweenGroups" value="true"/> | |
250 | * </module> | |
251 | * </pre> | |
252 | * <p> | |
253 | * It is possible to enforce | |
254 | * <a href="https://en.wikipedia.org/wiki/ASCII#Order">ASCII sort order</a> | |
255 | * of imports in groups using the following configuration: | |
256 | * </p> | |
257 | * <pre> | |
258 | * <module name="CustomImportOrder"> | |
259 | * <property name="sortImportsInGroupAlphabetically" value="true"/> | |
260 | * </module> | |
261 | * </pre> | |
262 | * <p> | |
263 | * Example of ASCII order: | |
264 | * </p> | |
265 | * <pre> | |
266 | * {@code | |
267 | *import java.awt.Dialog; | |
268 | *import java.awt.Window; | |
269 | *import java.awt.color.ColorSpace; | |
270 | *import java.awt.Frame; // violation here - in ASCII order 'F' should go before 'c', | |
271 | * // as all uppercase come before lowercase letters} | |
272 | * </pre> | |
273 | * <p> | |
274 | * To force checking imports sequence such as: | |
275 | * </p> | |
276 | * | |
277 | * <pre> | |
278 | * {@code | |
279 | * package com.puppycrawl.tools.checkstyle.imports; | |
280 | * | |
281 | * import com.google.common.annotations.GwtCompatible; | |
282 | * import com.google.common.annotations.Beta; | |
283 | * import com.google.common.annotations.VisibleForTesting; | |
284 | * | |
285 | * import org.abego.treelayout.Configuration; | |
286 | * | |
287 | * import static sun.tools.util.ModifierFilter.ALL_ACCESS; | |
288 | * | |
289 | * import com.google.common.annotations.GwtCompatible; // violation here - should be in the | |
290 | * // THIRD_PARTY_PACKAGE group | |
291 | * import android.*;} | |
292 | * </pre> | |
293 | * configure as follows: | |
294 | * <pre> | |
295 | * <module name="CustomImportOrder"> | |
296 | * <property name="customImportOrderRules" | |
297 | * value="SAME_PACKAGE(3)###THIRD_PARTY_PACKAGE###STATIC###SPECIAL_IMPORTS"/> | |
298 | * <property name="specialImportsRegExp" value="android.*"/> | |
299 | * </module> | |
300 | * </pre> | |
301 | * | |
302 | * @author maxvetrenko | |
303 | * @author <a href="mailto:nesterenko-aleksey@list.ru">Aleksey Nesterenko</a> | |
304 | */ | |
305 | public class CustomImportOrderCheck extends AbstractCheck { | |
306 | ||
307 | /** | |
308 | * A key is pointing to the warning message text in "messages.properties" | |
309 | * file. | |
310 | */ | |
311 | public static final String MSG_LINE_SEPARATOR = "custom.import.order.line.separator"; | |
312 | ||
313 | /** | |
314 | * A key is pointing to the warning message text in "messages.properties" | |
315 | * file. | |
316 | */ | |
317 | public static final String MSG_LEX = "custom.import.order.lex"; | |
318 | ||
319 | /** | |
320 | * A key is pointing to the warning message text in "messages.properties" | |
321 | * file. | |
322 | */ | |
323 | public static final String MSG_NONGROUP_IMPORT = "custom.import.order.nonGroup.import"; | |
324 | ||
325 | /** | |
326 | * A key is pointing to the warning message text in "messages.properties" | |
327 | * file. | |
328 | */ | |
329 | public static final String MSG_NONGROUP_EXPECTED = "custom.import.order.nonGroup.expected"; | |
330 | ||
331 | /** | |
332 | * A key is pointing to the warning message text in "messages.properties" | |
333 | * file. | |
334 | */ | |
335 | public static final String MSG_ORDER = "custom.import.order"; | |
336 | ||
337 | /** STATIC group name. */ | |
338 | public static final String STATIC_RULE_GROUP = "STATIC"; | |
339 | ||
340 | /** SAME_PACKAGE group name. */ | |
341 | public static final String SAME_PACKAGE_RULE_GROUP = "SAME_PACKAGE"; | |
342 | ||
343 | /** THIRD_PARTY_PACKAGE group name. */ | |
344 | public static final String THIRD_PARTY_PACKAGE_RULE_GROUP = "THIRD_PARTY_PACKAGE"; | |
345 | ||
346 | /** STANDARD_JAVA_PACKAGE group name. */ | |
347 | public static final String STANDARD_JAVA_PACKAGE_RULE_GROUP = "STANDARD_JAVA_PACKAGE"; | |
348 | ||
349 | /** SPECIAL_IMPORTS group name. */ | |
350 | public static final String SPECIAL_IMPORTS_RULE_GROUP = "SPECIAL_IMPORTS"; | |
351 | ||
352 | /** NON_GROUP group name. */ | |
353 | private static final String NON_GROUP_RULE_GROUP = "NOT_ASSIGNED_TO_ANY_GROUP"; | |
354 | ||
355 | /** Pattern used to separate groups of imports. */ | |
356 | private static final Pattern GROUP_SEPARATOR_PATTERN = Pattern.compile("\\s*###\\s*"); | |
357 | ||
358 | /** List of order declaration customizing by user. */ | |
359 | private final List<String> customImportOrderRules = new ArrayList<>(); | |
360 | ||
361 | /** Contains objects with import attributes. */ | |
362 | private final List<ImportDetails> importToGroupList = new ArrayList<>(); | |
363 | ||
364 | /** RegExp for SAME_PACKAGE group imports. */ | |
365 | private String samePackageDomainsRegExp = ""; | |
366 | ||
367 | /** RegExp for STANDARD_JAVA_PACKAGE group imports. */ | |
368 | private Pattern standardPackageRegExp = Pattern.compile("^(java|javax)\\."); | |
369 | ||
370 | /** RegExp for THIRD_PARTY_PACKAGE group imports. */ | |
371 | private Pattern thirdPartyPackageRegExp = Pattern.compile(".*"); | |
372 | ||
373 | /** RegExp for SPECIAL_IMPORTS group imports. */ | |
374 | private Pattern specialImportsRegExp = Pattern.compile("^$"); | |
375 | ||
376 | /** Force empty line separator between import groups. */ | |
377 | private boolean separateLineBetweenGroups = true; | |
378 | ||
379 | /** Force grouping alphabetically, in ASCII order. */ | |
380 | private boolean sortImportsInGroupAlphabetically; | |
381 | ||
382 | /** Number of first domains for SAME_PACKAGE group. */ | |
383 | private int samePackageMatchingDepth = 2; | |
384 | ||
385 | /** | |
386 | * Sets standardRegExp specified by user. | |
387 | * @param regexp | |
388 | * user value. | |
389 | */ | |
390 | public final void setStandardPackageRegExp(Pattern regexp) { | |
391 | standardPackageRegExp = regexp; | |
392 | } | |
393 | ||
394 | /** | |
395 | * Sets thirdPartyRegExp specified by user. | |
396 | * @param regexp | |
397 | * user value. | |
398 | */ | |
399 | public final void setThirdPartyPackageRegExp(Pattern regexp) { | |
400 | thirdPartyPackageRegExp = regexp; | |
401 | } | |
402 | ||
403 | /** | |
404 | * Sets specialImportsRegExp specified by user. | |
405 | * @param regexp | |
406 | * user value. | |
407 | */ | |
408 | public final void setSpecialImportsRegExp(Pattern regexp) { | |
409 | specialImportsRegExp = regexp; | |
410 | } | |
411 | ||
412 | /** | |
413 | * Sets separateLineBetweenGroups specified by user. | |
414 | * @param value | |
415 | * user value. | |
416 | */ | |
417 | public final void setSeparateLineBetweenGroups(boolean value) { | |
418 | separateLineBetweenGroups = value; | |
419 | } | |
420 | ||
421 | /** | |
422 | * Sets sortImportsInGroupAlphabetically specified by user. | |
423 | * @param value | |
424 | * user value. | |
425 | */ | |
426 | public final void setSortImportsInGroupAlphabetically(boolean value) { | |
427 | sortImportsInGroupAlphabetically = value; | |
428 | } | |
429 | ||
430 | /** | |
431 | * Sets a custom import order from the rules in the string format specified | |
432 | * by user. | |
433 | * @param inputCustomImportOrder | |
434 | * user value. | |
435 | */ | |
436 | public final void setCustomImportOrderRules(final String inputCustomImportOrder) { | |
437 |
1
1. setCustomImportOrderRules : removed call to java/util/List::clear → SURVIVED |
customImportOrderRules.clear(); |
438 |
3
1. setCustomImportOrderRules : changed conditional boundary → KILLED 2. setCustomImportOrderRules : Changed increment from 1 to -1 → KILLED 3. setCustomImportOrderRules : negated conditional → KILLED |
for (String currentState : GROUP_SEPARATOR_PATTERN.split(inputCustomImportOrder)) { |
439 |
1
1. setCustomImportOrderRules : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::addRulesToList → KILLED |
addRulesToList(currentState); |
440 | } | |
441 | customImportOrderRules.add(NON_GROUP_RULE_GROUP); | |
442 | } | |
443 | ||
444 | @Override | |
445 | public int[] getDefaultTokens() { | |
446 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getAcceptableTokens(); |
447 | } | |
448 | ||
449 | @Override | |
450 | public int[] getAcceptableTokens() { | |
451 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
452 | TokenTypes.IMPORT, | |
453 | TokenTypes.STATIC_IMPORT, | |
454 | TokenTypes.PACKAGE_DEF, | |
455 | }; | |
456 | } | |
457 | ||
458 | @Override | |
459 | public int[] getRequiredTokens() { | |
460 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getAcceptableTokens(); |
461 | } | |
462 | ||
463 | @Override | |
464 | public void beginTree(DetailAST rootAST) { | |
465 |
1
1. beginTree : removed call to java/util/List::clear → SURVIVED |
importToGroupList.clear(); |
466 | } | |
467 | ||
468 | @Override | |
469 | public void visitToken(DetailAST ast) { | |
470 |
1
1. visitToken : negated conditional → KILLED |
if (ast.getType() == TokenTypes.PACKAGE_DEF) { |
471 |
1
1. visitToken : negated conditional → KILLED |
if (customImportOrderRules.contains(SAME_PACKAGE_RULE_GROUP)) { |
472 | samePackageDomainsRegExp = createSamePackageRegexp( | |
473 | samePackageMatchingDepth, ast); | |
474 | } | |
475 | } | |
476 | else { | |
477 | final String importFullPath = getFullImportIdent(ast); | |
478 | final int lineNo = ast.getLineNo(); | |
479 |
1
1. visitToken : negated conditional → KILLED |
final boolean isStatic = ast.getType() == TokenTypes.STATIC_IMPORT; |
480 | importToGroupList.add(new ImportDetails(importFullPath, | |
481 | lineNo, getImportGroup(isStatic, importFullPath), | |
482 | isStatic)); | |
483 | } | |
484 | } | |
485 | ||
486 | @Override | |
487 | public void finishTree(DetailAST rootAST) { | |
488 | ||
489 |
1
1. finishTree : negated conditional → KILLED |
if (!importToGroupList.isEmpty()) { |
490 |
1
1. finishTree : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::finishImportList → KILLED |
finishImportList(); |
491 | } | |
492 | } | |
493 | ||
494 | /** Examine the order of all the imports and log any violations. */ | |
495 | private void finishImportList() { | |
496 | final ImportDetails firstImport = importToGroupList.get(0); | |
497 | String currentGroup = getImportGroup(firstImport.isStaticImport(), | |
498 | firstImport.getImportFullPath()); | |
499 | int currentGroupNumber = customImportOrderRules.indexOf(currentGroup); | |
500 | String previousImportFromCurrentGroup = null; | |
501 | ||
502 |
1
1. finishImportList : negated conditional → KILLED |
for (ImportDetails importObject : importToGroupList) { |
503 | final String importGroup = importObject.getImportGroup(); | |
504 | final String fullImportIdent = importObject.getImportFullPath(); | |
505 | ||
506 |
2
1. finishImportList : changed conditional boundary → KILLED 2. finishImportList : negated conditional → KILLED |
if (getCountOfEmptyLinesBefore(importObject.getLineNumber()) > 1) { |
507 |
1
1. finishImportList : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::log → KILLED |
log(importObject.getLineNumber(), MSG_LINE_SEPARATOR, fullImportIdent); |
508 | } | |
509 |
1
1. finishImportList : negated conditional → KILLED |
if (importGroup.equals(currentGroup)) { |
510 |
2
1. finishImportList : negated conditional → KILLED 2. finishImportList : negated conditional → KILLED |
if (sortImportsInGroupAlphabetically |
511 | && previousImportFromCurrentGroup != null | |
512 |
2
1. finishImportList : changed conditional boundary → KILLED 2. finishImportList : negated conditional → KILLED |
&& compareImports(fullImportIdent, previousImportFromCurrentGroup) < 0) { |
513 |
1
1. finishImportList : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::log → KILLED |
log(importObject.getLineNumber(), MSG_LEX, |
514 | fullImportIdent, previousImportFromCurrentGroup); | |
515 | } | |
516 | else { | |
517 | previousImportFromCurrentGroup = fullImportIdent; | |
518 | } | |
519 | } | |
520 | else { | |
521 | //not the last group, last one is always NON_GROUP | |
522 |
3
1. finishImportList : changed conditional boundary → KILLED 2. finishImportList : Replaced integer addition with subtraction → KILLED 3. finishImportList : negated conditional → KILLED |
if (customImportOrderRules.size() > currentGroupNumber + 1) { |
523 |
1
1. finishImportList : Replaced integer addition with subtraction → KILLED |
final String nextGroup = getNextImportGroup(currentGroupNumber + 1); |
524 |
1
1. finishImportList : negated conditional → KILLED |
if (importGroup.equals(nextGroup)) { |
525 |
1
1. finishImportList : negated conditional → KILLED |
if (separateLineBetweenGroups |
526 |
1
1. finishImportList : negated conditional → KILLED |
&& getCountOfEmptyLinesBefore(importObject.getLineNumber()) == 0) { |
527 |
1
1. finishImportList : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::log → KILLED |
log(importObject.getLineNumber(), MSG_LINE_SEPARATOR, fullImportIdent); |
528 | } | |
529 | currentGroup = nextGroup; | |
530 | currentGroupNumber = customImportOrderRules.indexOf(nextGroup); | |
531 | previousImportFromCurrentGroup = fullImportIdent; | |
532 | } | |
533 | else { | |
534 |
1
1. finishImportList : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::logWrongImportGroupOrder → KILLED |
logWrongImportGroupOrder(importObject.getLineNumber(), |
535 | importGroup, nextGroup, fullImportIdent); | |
536 | } | |
537 | } | |
538 | else { | |
539 |
1
1. finishImportList : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::logWrongImportGroupOrder → KILLED |
logWrongImportGroupOrder(importObject.getLineNumber(), |
540 | importGroup, currentGroup, fullImportIdent); | |
541 | } | |
542 | } | |
543 | } | |
544 | } | |
545 | ||
546 | /** | |
547 | * Log wrong import group order. | |
548 | * @param currentImportLine | |
549 | * line number of current import current import. | |
550 | * @param importGroup | |
551 | * import group. | |
552 | * @param currentGroupNumber | |
553 | * current group number we are checking. | |
554 | * @param fullImportIdent | |
555 | * full import name. | |
556 | */ | |
557 | private void logWrongImportGroupOrder(int currentImportLine, String importGroup, | |
558 | String currentGroupNumber, String fullImportIdent) { | |
559 |
1
1. logWrongImportGroupOrder : negated conditional → KILLED |
if (NON_GROUP_RULE_GROUP.equals(importGroup)) { |
560 |
1
1. logWrongImportGroupOrder : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::log → KILLED |
log(currentImportLine, MSG_NONGROUP_IMPORT, fullImportIdent); |
561 | } | |
562 |
1
1. logWrongImportGroupOrder : negated conditional → KILLED |
else if (NON_GROUP_RULE_GROUP.equals(currentGroupNumber)) { |
563 |
1
1. logWrongImportGroupOrder : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::log → KILLED |
log(currentImportLine, MSG_NONGROUP_EXPECTED, importGroup, fullImportIdent); |
564 | } | |
565 | else { | |
566 |
1
1. logWrongImportGroupOrder : removed call to com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::log → KILLED |
log(currentImportLine, MSG_ORDER, importGroup, currentGroupNumber, fullImportIdent); |
567 | } | |
568 | } | |
569 | ||
570 | /** | |
571 | * Get next import group. | |
572 | * @param currentGroupNumber | |
573 | * current group number. | |
574 | * @return | |
575 | * next import group. | |
576 | */ | |
577 | private String getNextImportGroup(int currentGroupNumber) { | |
578 | int nextGroupNumber = currentGroupNumber; | |
579 | ||
580 |
3
1. getNextImportGroup : changed conditional boundary → KILLED 2. getNextImportGroup : Replaced integer addition with subtraction → KILLED 3. getNextImportGroup : negated conditional → KILLED |
while (customImportOrderRules.size() > nextGroupNumber + 1) { |
581 |
1
1. getNextImportGroup : negated conditional → KILLED |
if (hasAnyImportInCurrentGroup(customImportOrderRules.get(nextGroupNumber))) { |
582 | break; | |
583 | } | |
584 |
1
1. getNextImportGroup : Changed increment from 1 to -1 → KILLED |
nextGroupNumber++; |
585 | } | |
586 |
1
1. getNextImportGroup : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::getNextImportGroup to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return customImportOrderRules.get(nextGroupNumber); |
587 | } | |
588 | ||
589 | /** | |
590 | * Checks if current group contains any import. | |
591 | * @param currentGroup | |
592 | * current group. | |
593 | * @return | |
594 | * true, if current group contains at least one import. | |
595 | */ | |
596 | private boolean hasAnyImportInCurrentGroup(String currentGroup) { | |
597 | boolean result = false; | |
598 |
1
1. hasAnyImportInCurrentGroup : negated conditional → KILLED |
for (ImportDetails currentImport : importToGroupList) { |
599 |
1
1. hasAnyImportInCurrentGroup : negated conditional → KILLED |
if (currentGroup.equals(currentImport.getImportGroup())) { |
600 | result = true; | |
601 | break; | |
602 | } | |
603 | } | |
604 |
1
1. hasAnyImportInCurrentGroup : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
605 | } | |
606 | ||
607 | /** | |
608 | * Get import valid group. | |
609 | * @param isStatic | |
610 | * is static import. | |
611 | * @param importPath | |
612 | * full import path. | |
613 | * @return import valid group. | |
614 | */ | |
615 | private String getImportGroup(boolean isStatic, String importPath) { | |
616 | RuleMatchForImport bestMatch = new RuleMatchForImport(NON_GROUP_RULE_GROUP, 0, 0); | |
617 |
2
1. getImportGroup : negated conditional → KILLED 2. getImportGroup : negated conditional → KILLED |
if (isStatic && customImportOrderRules.contains(STATIC_RULE_GROUP)) { |
618 | bestMatch.group = STATIC_RULE_GROUP; | |
619 | bestMatch.matchLength = importPath.length(); | |
620 | } | |
621 |
1
1. getImportGroup : negated conditional → KILLED |
else if (customImportOrderRules.contains(SAME_PACKAGE_RULE_GROUP)) { |
622 | final String importPathTrimmedToSamePackageDepth = | |
623 | getFirstDomainsFromIdent(samePackageMatchingDepth, importPath); | |
624 |
1
1. getImportGroup : negated conditional → KILLED |
if (samePackageDomainsRegExp.equals(importPathTrimmedToSamePackageDepth)) { |
625 | bestMatch.group = SAME_PACKAGE_RULE_GROUP; | |
626 | bestMatch.matchLength = importPath.length(); | |
627 | } | |
628 | } | |
629 |
1
1. getImportGroup : negated conditional → KILLED |
if (bestMatch.group.equals(NON_GROUP_RULE_GROUP)) { |
630 |
1
1. getImportGroup : negated conditional → KILLED |
for (String group : customImportOrderRules) { |
631 |
1
1. getImportGroup : negated conditional → KILLED |
if (STANDARD_JAVA_PACKAGE_RULE_GROUP.equals(group)) { |
632 | bestMatch = findBetterPatternMatch(importPath, | |
633 | STANDARD_JAVA_PACKAGE_RULE_GROUP, standardPackageRegExp, bestMatch); | |
634 | } | |
635 |
1
1. getImportGroup : negated conditional → SURVIVED |
if (SPECIAL_IMPORTS_RULE_GROUP.equals(group)) { |
636 | bestMatch = findBetterPatternMatch(importPath, | |
637 | SPECIAL_IMPORTS_RULE_GROUP, specialImportsRegExp, bestMatch); | |
638 | } | |
639 | } | |
640 | } | |
641 |
1
1. getImportGroup : negated conditional → KILLED |
if (bestMatch.group.equals(NON_GROUP_RULE_GROUP) |
642 |
1
1. getImportGroup : negated conditional → KILLED |
&& customImportOrderRules.contains(THIRD_PARTY_PACKAGE_RULE_GROUP) |
643 |
1
1. getImportGroup : negated conditional → KILLED |
&& thirdPartyPackageRegExp.matcher(importPath).find()) { |
644 | bestMatch.group = THIRD_PARTY_PACKAGE_RULE_GROUP; | |
645 | } | |
646 |
1
1. getImportGroup : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::getImportGroup to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return bestMatch.group; |
647 | } | |
648 | ||
649 | /** Tries to find better matching regular expression: | |
650 | * longer matching substring wins; in case of the same length, | |
651 | * lower position of matching substring wins. | |
652 | * @param importPath | |
653 | * Full import identifier | |
654 | * @param group | |
655 | * Import group we are trying to assign the import | |
656 | * @param regExp | |
657 | * Regular expression for import group | |
658 | * @param currentBestMatch | |
659 | * object with currently best match | |
660 | * @return better match (if found) or the same (currentBestMatch) | |
661 | */ | |
662 | private static RuleMatchForImport findBetterPatternMatch(String importPath, String group, | |
663 | Pattern regExp, RuleMatchForImport currentBestMatch) { | |
664 | RuleMatchForImport betterMatchCandidate = currentBestMatch; | |
665 | final Matcher matcher = regExp.matcher(importPath); | |
666 |
1
1. findBetterPatternMatch : negated conditional → KILLED |
while (matcher.find()) { |
667 |
1
1. findBetterPatternMatch : Replaced integer subtraction with addition → SURVIVED |
final int length = matcher.end() - matcher.start(); |
668 |
2
1. findBetterPatternMatch : changed conditional boundary → SURVIVED 2. findBetterPatternMatch : negated conditional → KILLED |
if (length > betterMatchCandidate.matchLength |
669 |
1
1. findBetterPatternMatch : negated conditional → SURVIVED |
|| length == betterMatchCandidate.matchLength |
670 |
2
1. findBetterPatternMatch : changed conditional boundary → SURVIVED 2. findBetterPatternMatch : negated conditional → SURVIVED |
&& matcher.start() < betterMatchCandidate.matchPosition) { |
671 | betterMatchCandidate = new RuleMatchForImport(group, length, matcher.start()); | |
672 | } | |
673 | } | |
674 |
1
1. findBetterPatternMatch : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::findBetterPatternMatch to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return betterMatchCandidate; |
675 | } | |
676 | ||
677 | /** | |
678 | * Checks compare two import paths. | |
679 | * @param import1 | |
680 | * current import. | |
681 | * @param import2 | |
682 | * previous import. | |
683 | * @return a negative integer, zero, or a positive integer as the | |
684 | * specified String is greater than, equal to, or less | |
685 | * than this String, ignoring case considerations. | |
686 | */ | |
687 | private static int compareImports(String import1, String import2) { | |
688 | int result = 0; | |
689 | final String separator = "\\."; | |
690 | final String[] import1Tokens = import1.split(separator); | |
691 | final String[] import2Tokens = import2.split(separator); | |
692 |
4
1. compareImports : changed conditional boundary → SURVIVED 2. compareImports : Changed increment from 1 to -1 → KILLED 3. compareImports : negated conditional → KILLED 4. compareImports : negated conditional → KILLED |
for (int i = 0; i < import1Tokens.length && i != import2Tokens.length; i++) { |
693 | final String import1Token = import1Tokens[i]; | |
694 | final String import2Token = import2Tokens[i]; | |
695 | result = import1Token.compareTo(import2Token); | |
696 |
1
1. compareImports : negated conditional → KILLED |
if (result != 0) { |
697 | break; | |
698 | } | |
699 | } | |
700 |
1
1. compareImports : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
701 | } | |
702 | ||
703 | /** | |
704 | * Counts empty lines before given. | |
705 | * @param lineNo | |
706 | * Line number of current import. | |
707 | * @return count of empty lines before given. | |
708 | */ | |
709 | private int getCountOfEmptyLinesBefore(int lineNo) { | |
710 | int result = 0; | |
711 | final String[] lines = getLines(); | |
712 | // [lineNo - 2] is the number of the previous line | |
713 | // because the numbering starts from zero. | |
714 |
1
1. getCountOfEmptyLinesBefore : Replaced integer subtraction with addition → KILLED |
int lineBeforeIndex = lineNo - 2; |
715 |
2
1. getCountOfEmptyLinesBefore : changed conditional boundary → KILLED 2. getCountOfEmptyLinesBefore : negated conditional → KILLED |
while (lineBeforeIndex >= 0 |
716 |
1
1. getCountOfEmptyLinesBefore : negated conditional → KILLED |
&& CommonUtils.isBlank(lines[lineBeforeIndex])) { |
717 |
1
1. getCountOfEmptyLinesBefore : Changed increment from -1 to 1 → KILLED |
lineBeforeIndex--; |
718 |
1
1. getCountOfEmptyLinesBefore : Changed increment from 1 to -1 → KILLED |
result++; |
719 | } | |
720 |
1
1. getCountOfEmptyLinesBefore : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return result; |
721 | } | |
722 | ||
723 | /** | |
724 | * Forms import full path. | |
725 | * @param token | |
726 | * current token. | |
727 | * @return full path or null. | |
728 | */ | |
729 | private static String getFullImportIdent(DetailAST token) { | |
730 | String ident = ""; | |
731 |
1
1. getFullImportIdent : negated conditional → KILLED |
if (token != null) { |
732 | ident = FullIdent.createFullIdent(token.findFirstToken(TokenTypes.DOT)).getText(); | |
733 | } | |
734 |
1
1. getFullImportIdent : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::getFullImportIdent to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return ident; |
735 | } | |
736 | ||
737 | /** | |
738 | * Parses ordering rule and adds it to the list with rules. | |
739 | * @param ruleStr | |
740 | * String with rule. | |
741 | */ | |
742 | private void addRulesToList(String ruleStr) { | |
743 |
1
1. addRulesToList : negated conditional → KILLED |
if (STATIC_RULE_GROUP.equals(ruleStr) |
744 |
1
1. addRulesToList : negated conditional → KILLED |
|| THIRD_PARTY_PACKAGE_RULE_GROUP.equals(ruleStr) |
745 |
1
1. addRulesToList : negated conditional → KILLED |
|| STANDARD_JAVA_PACKAGE_RULE_GROUP.equals(ruleStr) |
746 |
1
1. addRulesToList : negated conditional → KILLED |
|| SPECIAL_IMPORTS_RULE_GROUP.equals(ruleStr)) { |
747 | customImportOrderRules.add(ruleStr); | |
748 | ||
749 | } | |
750 |
1
1. addRulesToList : negated conditional → KILLED |
else if (ruleStr.startsWith(SAME_PACKAGE_RULE_GROUP)) { |
751 | ||
752 |
1
1. addRulesToList : Replaced integer addition with subtraction → KILLED |
final String rule = ruleStr.substring(ruleStr.indexOf('(') + 1, |
753 | ruleStr.indexOf(')')); | |
754 | samePackageMatchingDepth = Integer.parseInt(rule); | |
755 |
2
1. addRulesToList : changed conditional boundary → KILLED 2. addRulesToList : negated conditional → KILLED |
if (samePackageMatchingDepth <= 0) { |
756 | throw new IllegalArgumentException( | |
757 | "SAME_PACKAGE rule parameter should be positive integer: " + ruleStr); | |
758 | } | |
759 | customImportOrderRules.add(SAME_PACKAGE_RULE_GROUP); | |
760 | ||
761 | } | |
762 | else { | |
763 | throw new IllegalStateException("Unexpected rule: " + ruleStr); | |
764 | } | |
765 | } | |
766 | ||
767 | /** | |
768 | * Creates samePackageDomainsRegExp of the first package domains. | |
769 | * @param firstPackageDomainsCount | |
770 | * number of first package domains. | |
771 | * @param packageNode | |
772 | * package node. | |
773 | * @return same package regexp. | |
774 | */ | |
775 | private static String createSamePackageRegexp(int firstPackageDomainsCount, | |
776 | DetailAST packageNode) { | |
777 | final String packageFullPath = getFullImportIdent(packageNode); | |
778 |
1
1. createSamePackageRegexp : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::createSamePackageRegexp to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getFirstDomainsFromIdent(firstPackageDomainsCount, packageFullPath); |
779 | } | |
780 | ||
781 | /** | |
782 | * Extracts defined amount of domains from the left side of package/import identifier. | |
783 | * @param firstPackageDomainsCount | |
784 | * number of first package domains. | |
785 | * @param packageFullPath | |
786 | * full identifier containing path to package or imported object. | |
787 | * @return String with defined amount of domains or full identifier | |
788 | * (if full identifier had less domain then specified) | |
789 | */ | |
790 | private static String getFirstDomainsFromIdent( | |
791 | final int firstPackageDomainsCount, final String packageFullPath) { | |
792 | final StringBuilder builder = new StringBuilder(256); | |
793 | final StringTokenizer tokens = new StringTokenizer(packageFullPath, "."); | |
794 | int count = firstPackageDomainsCount; | |
795 | ||
796 |
3
1. getFirstDomainsFromIdent : changed conditional boundary → KILLED 2. getFirstDomainsFromIdent : negated conditional → KILLED 3. getFirstDomainsFromIdent : negated conditional → KILLED |
while (count > 0 && tokens.hasMoreTokens()) { |
797 | builder.append(tokens.nextToken()).append('.'); | |
798 |
1
1. getFirstDomainsFromIdent : Changed increment from -1 to 1 → KILLED |
count--; |
799 | } | |
800 |
1
1. getFirstDomainsFromIdent : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck::getFirstDomainsFromIdent to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return builder.toString(); |
801 | } | |
802 | ||
803 | /** | |
804 | * Contains import attributes as line number, import full path, import | |
805 | * group. | |
806 | * @author max | |
807 | */ | |
808 | private static class ImportDetails { | |
809 | /** Import full path. */ | |
810 | private final String importFullPath; | |
811 | ||
812 | /** Import line number. */ | |
813 | private final int lineNumber; | |
814 | ||
815 | /** Import group. */ | |
816 | private final String importGroup; | |
817 | ||
818 | /** Is static import. */ | |
819 | private final boolean staticImport; | |
820 | ||
821 | /** | |
822 | * Initialise importFullPath, lineNumber, importGroup, staticImport. | |
823 | * @param importFullPath | |
824 | * import full path. | |
825 | * @param lineNumber | |
826 | * import line number. | |
827 | * @param importGroup | |
828 | * import group. | |
829 | * @param staticImport | |
830 | * if import is static. | |
831 | */ | |
832 | ImportDetails(String importFullPath, | |
833 | int lineNumber, String importGroup, boolean staticImport) { | |
834 | this.importFullPath = importFullPath; | |
835 | this.lineNumber = lineNumber; | |
836 | this.importGroup = importGroup; | |
837 | this.staticImport = staticImport; | |
838 | } | |
839 | ||
840 | /** | |
841 | * Get import full path variable. | |
842 | * @return import full path variable. | |
843 | */ | |
844 | public String getImportFullPath() { | |
845 |
1
1. getImportFullPath : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck$ImportDetails::getImportFullPath to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return importFullPath; |
846 | } | |
847 | ||
848 | /** | |
849 | * Get import line number. | |
850 | * @return import line. | |
851 | */ | |
852 | public int getLineNumber() { | |
853 |
1
1. getLineNumber : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return lineNumber; |
854 | } | |
855 | ||
856 | /** | |
857 | * Get import group. | |
858 | * @return import group. | |
859 | */ | |
860 | public String getImportGroup() { | |
861 |
1
1. getImportGroup : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/imports/CustomImportOrderCheck$ImportDetails::getImportGroup to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return importGroup; |
862 | } | |
863 | ||
864 | /** | |
865 | * Checks if import is static. | |
866 | * @return true, if import is static. | |
867 | */ | |
868 | public boolean isStaticImport() { | |
869 |
1
1. isStaticImport : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return staticImport; |
870 | } | |
871 | } | |
872 | ||
873 | /** | |
874 | * Contains matching attributes assisting in definition of "best matching" | |
875 | * group for import. | |
876 | * @author ivanov-alex | |
877 | */ | |
878 | private static class RuleMatchForImport { | |
879 | /** Position of matching string for current best match. */ | |
880 | private final int matchPosition; | |
881 | /** Length of matching string for current best match. */ | |
882 | private int matchLength; | |
883 | /** Import group for current best match. */ | |
884 | private String group; | |
885 | ||
886 | /** Constructor to initialize the fields. | |
887 | * @param group | |
888 | * Matched group. | |
889 | * @param length | |
890 | * Matching length. | |
891 | * @param position | |
892 | * Matching position. | |
893 | */ | |
894 | RuleMatchForImport(String group, int length, int position) { | |
895 | this.group = group; | |
896 | matchLength = length; | |
897 | matchPosition = position; | |
898 | } | |
899 | } | |
900 | } | |
Mutations | ||
437 |
1.1 |
|
438 |
1.1 2.2 3.3 |
|
439 |
1.1 |
|
446 |
1.1 |
|
451 |
1.1 |
|
460 |
1.1 |
|
465 |
1.1 |
|
470 |
1.1 |
|
471 |
1.1 |
|
479 |
1.1 |
|
489 |
1.1 |
|
490 |
1.1 |
|
502 |
1.1 |
|
506 |
1.1 2.2 |
|
507 |
1.1 |
|
509 |
1.1 |
|
510 |
1.1 2.2 |
|
512 |
1.1 2.2 |
|
513 |
1.1 |
|
522 |
1.1 2.2 3.3 |
|
523 |
1.1 |
|
524 |
1.1 |
|
525 |
1.1 |
|
526 |
1.1 |
|
527 |
1.1 |
|
534 |
1.1 |
|
539 |
1.1 |
|
559 |
1.1 |
|
560 |
1.1 |
|
562 |
1.1 |
|
563 |
1.1 |
|
566 |
1.1 |
|
580 |
1.1 2.2 3.3 |
|
581 |
1.1 |
|
584 |
1.1 |
|
586 |
1.1 |
|
598 |
1.1 |
|
599 |
1.1 |
|
604 |
1.1 |
|
617 |
1.1 2.2 |
|
621 |
1.1 |
|
624 |
1.1 |
|
629 |
1.1 |
|
630 |
1.1 |
|
631 |
1.1 |
|
635 |
1.1 |
|
641 |
1.1 |
|
642 |
1.1 |
|
643 |
1.1 |
|
646 |
1.1 |
|
666 |
1.1 |
|
667 |
1.1 |
|
668 |
1.1 2.2 |
|
669 |
1.1 |
|
670 |
1.1 2.2 |
|
674 |
1.1 |
|
692 |
1.1 2.2 3.3 4.4 |
|
696 |
1.1 |
|
700 |
1.1 |
|
714 |
1.1 |
|
715 |
1.1 2.2 |
|
716 |
1.1 |
|
717 |
1.1 |
|
718 |
1.1 |
|
720 |
1.1 |
|
731 |
1.1 |
|
734 |
1.1 |
|
743 |
1.1 |
|
744 |
1.1 |
|
745 |
1.1 |
|
746 |
1.1 |
|
750 |
1.1 |
|
752 |
1.1 |
|
755 |
1.1 2.2 |
|
778 |
1.1 |
|
796 |
1.1 2.2 3.3 |
|
798 |
1.1 |
|
800 |
1.1 |
|
845 |
1.1 |
|
853 |
1.1 |
|
861 |
1.1 |
|
869 |
1.1 |