1 | //////////////////////////////////////////////////////////////////////////////// | |
2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
3 | // Copyright (C) 2001-2018 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.coding; | |
21 | ||
22 | import java.util.ArrayList; | |
23 | import java.util.BitSet; | |
24 | import java.util.HashMap; | |
25 | import java.util.List; | |
26 | import java.util.Map; | |
27 | import java.util.regex.Pattern; | |
28 | ||
29 | import com.puppycrawl.tools.checkstyle.FileStatefulCheck; | |
30 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
31 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
32 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
33 | import com.puppycrawl.tools.checkstyle.utils.TokenUtils; | |
34 | ||
35 | /** | |
36 | * Checks for multiple occurrences of the same string literal within a | |
37 | * single file. | |
38 | * | |
39 | * @author Daniel Grenner | |
40 | */ | |
41 | @FileStatefulCheck | |
42 | public class MultipleStringLiteralsCheck extends AbstractCheck { | |
43 | ||
44 | /** | |
45 | * A key is pointing to the warning message text in "messages.properties" | |
46 | * file. | |
47 | */ | |
48 | public static final String MSG_KEY = "multiple.string.literal"; | |
49 | ||
50 | /** | |
51 | * The found strings and their positions. | |
52 | * {@code <String, ArrayList>}, with the ArrayList containing StringInfo | |
53 | * objects. | |
54 | */ | |
55 | private final Map<String, List<StringInfo>> stringMap = new HashMap<>(); | |
56 | ||
57 | /** | |
58 | * Marks the TokenTypes where duplicate strings should be ignored. | |
59 | */ | |
60 | private final BitSet ignoreOccurrenceContext = new BitSet(); | |
61 | ||
62 | /** | |
63 | * The allowed number of string duplicates in a file before an error is | |
64 | * generated. | |
65 | */ | |
66 | private int allowedDuplicates = 1; | |
67 | ||
68 | /** | |
69 | * Pattern for matching ignored strings. | |
70 | */ | |
71 | private Pattern ignoreStringsRegexp; | |
72 | ||
73 | /** | |
74 | * Construct an instance with default values. | |
75 | */ | |
76 | public MultipleStringLiteralsCheck() { | |
77 |
1
1. |
setIgnoreStringsRegexp(Pattern.compile("^\"\"$")); |
78 |
1
1. |
ignoreOccurrenceContext.set(TokenTypes.ANNOTATION); |
79 | } | |
80 | ||
81 | /** | |
82 | * Sets the maximum allowed duplicates of a string. | |
83 | * @param allowedDuplicates The maximum number of duplicates. | |
84 | */ | |
85 | public void setAllowedDuplicates(int allowedDuplicates) { | |
86 | this.allowedDuplicates = allowedDuplicates; | |
87 | } | |
88 | ||
89 | /** | |
90 | * Sets regular expression pattern for ignored strings. | |
91 | * @param ignoreStringsRegexp | |
92 | * regular expression pattern for ignored strings | |
93 | * @noinspection WeakerAccess | |
94 | */ | |
95 | public final void setIgnoreStringsRegexp(Pattern ignoreStringsRegexp) { | |
96 |
2
1. setIgnoreStringsRegexp : negated conditional → KILLED 2. setIgnoreStringsRegexp : negated conditional → KILLED |
if (ignoreStringsRegexp == null || ignoreStringsRegexp.pattern().isEmpty()) { |
97 | this.ignoreStringsRegexp = null; | |
98 | } | |
99 | else { | |
100 | this.ignoreStringsRegexp = ignoreStringsRegexp; | |
101 | } | |
102 | } | |
103 | ||
104 | /** | |
105 | * Adds a set of tokens the check is interested in. | |
106 | * @param strRep the string representation of the tokens interested in | |
107 | */ | |
108 | public final void setIgnoreOccurrenceContext(String... strRep) { | |
109 |
1
1. setIgnoreOccurrenceContext : removed call to java/util/BitSet::clear → KILLED |
ignoreOccurrenceContext.clear(); |
110 | for (final String s : strRep) { | |
111 | final int type = TokenUtils.getTokenId(s); | |
112 |
1
1. setIgnoreOccurrenceContext : removed call to java/util/BitSet::set → KILLED |
ignoreOccurrenceContext.set(type); |
113 | } | |
114 | } | |
115 | ||
116 | @Override | |
117 | public int[] getDefaultTokens() { | |
118 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getRequiredTokens(); |
119 | } | |
120 | ||
121 | @Override | |
122 | public int[] getAcceptableTokens() { | |
123 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getRequiredTokens(); |
124 | } | |
125 | ||
126 | @Override | |
127 | public int[] getRequiredTokens() { | |
128 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] {TokenTypes.STRING_LITERAL}; |
129 | } | |
130 | ||
131 | @Override | |
132 | public void visitToken(DetailAST ast) { | |
133 |
1
1. visitToken : negated conditional → KILLED |
if (!isInIgnoreOccurrenceContext(ast)) { |
134 | final String currentString = ast.getText(); | |
135 |
2
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED |
if (ignoreStringsRegexp == null || !ignoreStringsRegexp.matcher(currentString).find()) { |
136 | List<StringInfo> hitList = stringMap.get(currentString); | |
137 |
1
1. visitToken : negated conditional → KILLED |
if (hitList == null) { |
138 | hitList = new ArrayList<>(); | |
139 | stringMap.put(currentString, hitList); | |
140 | } | |
141 | final int line = ast.getLineNo(); | |
142 | final int col = ast.getColumnNo(); | |
143 | hitList.add(new StringInfo(line, col)); | |
144 | } | |
145 | } | |
146 | } | |
147 | ||
148 | /** | |
149 | * Analyses the path from the AST root to a given AST for occurrences | |
150 | * of the token types in {@link #ignoreOccurrenceContext}. | |
151 | * | |
152 | * @param ast the node from where to start searching towards the root node | |
153 | * @return whether the path from the root node to ast contains one of the | |
154 | * token type in {@link #ignoreOccurrenceContext}. | |
155 | */ | |
156 | private boolean isInIgnoreOccurrenceContext(DetailAST ast) { | |
157 | boolean isInIgnoreOccurrenceContext = false; | |
158 | for (DetailAST token = ast; | |
159 |
1
1. isInIgnoreOccurrenceContext : negated conditional → KILLED |
token.getParent() != null; |
160 | token = token.getParent()) { | |
161 | final int type = token.getType(); | |
162 |
1
1. isInIgnoreOccurrenceContext : negated conditional → KILLED |
if (ignoreOccurrenceContext.get(type)) { |
163 | isInIgnoreOccurrenceContext = true; | |
164 | break; | |
165 | } | |
166 | } | |
167 |
1
1. isInIgnoreOccurrenceContext : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return isInIgnoreOccurrenceContext; |
168 | } | |
169 | ||
170 | @Override | |
171 | public void beginTree(DetailAST rootAST) { | |
172 |
1
1. beginTree : removed call to java/util/Map::clear → KILLED |
stringMap.clear(); |
173 | } | |
174 | ||
175 | @Override | |
176 | public void finishTree(DetailAST rootAST) { | |
177 | for (Map.Entry<String, List<StringInfo>> stringListEntry : stringMap.entrySet()) { | |
178 | final List<StringInfo> hits = stringListEntry.getValue(); | |
179 |
2
1. finishTree : changed conditional boundary → KILLED 2. finishTree : negated conditional → KILLED |
if (hits.size() > allowedDuplicates) { |
180 | final StringInfo firstFinding = hits.get(0); | |
181 | final int line = firstFinding.getLine(); | |
182 | final int col = firstFinding.getCol(); | |
183 |
1
1. finishTree : removed call to com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::log → KILLED |
log(line, col, MSG_KEY, stringListEntry.getKey(), hits.size()); |
184 | } | |
185 | } | |
186 | } | |
187 | ||
188 | /** | |
189 | * This class contains information about where a string was found. | |
190 | */ | |
191 | private static final class StringInfo { | |
192 | ||
193 | /** | |
194 | * Line of finding. | |
195 | */ | |
196 | private final int line; | |
197 | /** | |
198 | * Column of finding. | |
199 | */ | |
200 | private final int col; | |
201 | ||
202 | /** | |
203 | * Creates information about a string position. | |
204 | * @param line int | |
205 | * @param col int | |
206 | */ | |
207 | StringInfo(int line, int col) { | |
208 | this.line = line; | |
209 | this.col = col; | |
210 | } | |
211 | ||
212 | /** | |
213 | * The line where a string was found. | |
214 | * @return int Line of the string. | |
215 | */ | |
216 | private int getLine() { | |
217 |
1
1. getLine : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return line; |
218 | } | |
219 | ||
220 | /** | |
221 | * The column where a string was found. | |
222 | * @return int Column of the string. | |
223 | */ | |
224 | private int getCol() { | |
225 |
1
1. getCol : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return col; |
226 | } | |
227 | ||
228 | } | |
229 | ||
230 | } | |
Mutations | ||
77 |
1.1 |
|
78 |
1.1 |
|
96 |
1.1 2.2 |
|
109 |
1.1 |
|
112 |
1.1 |
|
118 |
1.1 |
|
123 |
1.1 |
|
128 |
1.1 |
|
133 |
1.1 |
|
135 |
1.1 2.2 |
|
137 |
1.1 |
|
159 |
1.1 |
|
162 |
1.1 |
|
167 |
1.1 |
|
172 |
1.1 |
|
179 |
1.1 2.2 |
|
183 |
1.1 |
|
217 |
1.1 |
|
225 |
1.1 |