MultipleStringLiteralsCheck.java

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. : removed call to com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::setIgnoreStringsRegexp → KILLED
        setIgnoreStringsRegexp(Pattern.compile("^\"\"$"));
78 1 1. : removed call to java/util/BitSet::set → KILLED
        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
Location :
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testItIgnoreEmpty(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::setIgnoreStringsRegexp → KILLED

78

1.1
Location :
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
removed call to java/util/BitSet::set → KILLED

96

1.1
Location : setIgnoreStringsRegexp
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testItIgnoreEmpty(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
negated conditional → KILLED

2.2
Location : setIgnoreStringsRegexp
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
negated conditional → KILLED

109

1.1
Location : setIgnoreOccurrenceContext
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIgnores(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
removed call to java/util/BitSet::clear → KILLED

112

1.1
Location : setIgnoreOccurrenceContext
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIgnores(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
removed call to java/util/BitSet::set → KILLED

118

1.1
Location : getDefaultTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testTokensNotNull(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

123

1.1
Location : getAcceptableTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testTokensNotNull(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

128

1.1
Location : getRequiredTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testTokensNotNull(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

133

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
negated conditional → KILLED

135

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
negated conditional → KILLED

2.2
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testItIgnoreEmpty(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
negated conditional → KILLED

137

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
negated conditional → KILLED

159

1.1
Location : isInIgnoreOccurrenceContext
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
negated conditional → KILLED

162

1.1
Location : isInIgnoreOccurrenceContext
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
negated conditional → KILLED

167

1.1
Location : isInIgnoreOccurrenceContext
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED

172

1.1
Location : beginTree
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testMultipleInputs(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
removed call to java/util/Map::clear → KILLED

179

1.1
Location : finishTree
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
changed conditional boundary → KILLED

2.2
Location : finishTree
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
negated conditional → KILLED

183

1.1
Location : finishTree
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/MultipleStringLiteralsCheck::log → KILLED

217

1.1
Location : getLine
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED

225

1.1
Location : getCol
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest.testIt(com.puppycrawl.tools.checkstyle.checks.coding.MultipleStringLiteralsCheckTest)
replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED

Active mutators

Tests examined


Report generated by PIT 1.3.1