• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / 277

27 Nov 2025 01:37PM UTC coverage: 78.778% (+0.03%) from 78.749%
277

push

github

adangel
[java] UseArraysAsList: skip when if-statements (#6228)

18419 of 24233 branches covered (76.01%)

Branch coverage included in aggregate %.

40090 of 50038 relevant lines covered (80.12%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.47
/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.lang.rule;
6

7
import java.lang.reflect.Constructor;
8
import java.lang.reflect.InvocationTargetException;
9
import java.util.ArrayList;
10
import java.util.Collections;
11
import java.util.LinkedHashSet;
12
import java.util.List;
13
import java.util.Objects;
14
import java.util.Set;
15

16
import org.checkerframework.checker.nullness.qual.NonNull;
17

18
import net.sourceforge.pmd.lang.Language;
19
import net.sourceforge.pmd.lang.LanguageVersion;
20
import net.sourceforge.pmd.lang.ast.Node;
21
import net.sourceforge.pmd.lang.ast.RootNode;
22
import net.sourceforge.pmd.properties.AbstractPropertySource;
23
import net.sourceforge.pmd.properties.PropertyDescriptor;
24
import net.sourceforge.pmd.reporting.InternalApiBridge;
25
import net.sourceforge.pmd.reporting.RuleContext;
26

27
/**
28
 * Basic abstract implementation of all parser-independent methods of the Rule
29
 * interface.
30
 *
31
 * @author pieter_van_raemdonck - Application Engineers NV/SA - www.ae.be
32
 */
33
public abstract class AbstractRule extends AbstractPropertySource implements Rule {
1✔
34

35
    private Language language;
36
    private LanguageVersion minimumLanguageVersion;
37
    private LanguageVersion maximumLanguageVersion;
38
    private boolean deprecated;
39
    private String name = getClass().getName();
1✔
40
    private String since;
41
    private String ruleClass = getClass().getName();
1✔
42
    private String ruleSetName;
43
    private String message;
44
    private String description;
45
    private List<String> examples = new ArrayList<>();
1✔
46
    private String externalInfoUrl;
47
    private RulePriority priority = RulePriority.LOW;
1✔
48
    private Set<String> ruleChainVisits = new LinkedHashSet<>();
1✔
49
    private Set<Class<? extends Node>> classRuleChainVisits = new LinkedHashSet<>();
1✔
50
    private RuleTargetSelector myStrategy;
51

52
    public AbstractRule() {
1✔
53
        definePropertyDescriptor(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR);
1✔
54
        definePropertyDescriptor(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR);
1✔
55
    }
1✔
56

57
    @Override
58
    protected String getPropertySourceType() {
59
        return "rule";
×
60
    }
61

62
    @Override
63
    public Language getLanguage() {
64
        return language;
1✔
65
    }
66

67
    @Override
68
    public void setLanguage(Language language) {
69
        if (this.language != null && !this.language.equals(language)) {
1✔
70
            throw new UnsupportedOperationException("The Language for Rule class " + this.getClass().getName()
1✔
71
                    + " is immutable and cannot be changed.");
72
        }
73
        this.language = language;
1✔
74
    }
1✔
75

76
    @Override
77
    public LanguageVersion getMinimumLanguageVersion() {
78
        return minimumLanguageVersion;
1✔
79
    }
80

81
    @Override
82
    public void setMinimumLanguageVersion(LanguageVersion minimumLanguageVersion) {
83
        if (minimumLanguageVersion != null && !minimumLanguageVersion.getLanguage().equals(getLanguage())) {
1✔
84
            throw new IllegalArgumentException("Version " + minimumLanguageVersion + " does not belong to language " + getLanguage());
1✔
85
        }
86
        this.minimumLanguageVersion = minimumLanguageVersion;
1✔
87
    }
1✔
88

89
    @Override
90
    public LanguageVersion getMaximumLanguageVersion() {
91
        return maximumLanguageVersion;
1✔
92
    }
93

94
    @Override
95
    public void setMaximumLanguageVersion(LanguageVersion maximumLanguageVersion) {
96
        if (maximumLanguageVersion != null && !maximumLanguageVersion.getLanguage().equals(getLanguage())) {
1✔
97
            throw new IllegalArgumentException("Version " + maximumLanguageVersion + " does not belong to language " + getLanguage());
1✔
98
        }
99
        this.maximumLanguageVersion = maximumLanguageVersion;
1✔
100
    }
1✔
101

102
    @Override
103
    public boolean isDeprecated() {
104
        return deprecated;
1✔
105
    }
106

107
    @Override
108
    public void setDeprecated(boolean deprecated) {
109
        this.deprecated = deprecated;
1✔
110
    }
1✔
111

112
    @Override
113
    public String getName() {
114
        return name;
1✔
115
    }
116

117
    @Override
118
    public void setName(String name) {
119
        this.name = name;
1✔
120
    }
1✔
121

122
    @Override
123
    public String getSince() {
124
        return since;
1✔
125
    }
126

127
    @Override
128
    public void setSince(String since) {
129
        this.since = since;
1✔
130
    }
1✔
131

132
    @Override
133
    public String getRuleClass() {
134
        return ruleClass;
1✔
135
    }
136

137
    @Override
138
    public void setRuleClass(String ruleClass) {
139
        this.ruleClass = ruleClass;
×
140
    }
×
141

142
    @Override
143
    public String getRuleSetName() {
144
        return ruleSetName;
1✔
145
    }
146

147
    @Override
148
    public void setRuleSetName(String ruleSetName) {
149
        this.ruleSetName = ruleSetName;
1✔
150
    }
1✔
151

152
    @Override
153
    public String getMessage() {
154
        return message;
1✔
155
    }
156

157
    @Override
158
    public void setMessage(String message) {
159
        this.message = message;
1✔
160
    }
1✔
161

162
    @Override
163
    public String getDescription() {
164
        return description;
1✔
165
    }
166

167
    @Override
168
    public void setDescription(String description) {
169
        this.description = description;
1✔
170
    }
1✔
171

172
    @Override
173
    public List<String> getExamples() {
174
        // TODO Needs to be externally immutable
175
        return examples;
1✔
176
    }
177

178
    @Override
179
    public void addExample(String example) {
180
        examples.add(example);
1✔
181
    }
1✔
182

183
    @Override
184
    public String getExternalInfoUrl() {
185
        return externalInfoUrl;
1✔
186
    }
187

188
    @Override
189
    public void setExternalInfoUrl(String externalInfoUrl) {
190
        this.externalInfoUrl = externalInfoUrl;
1✔
191
    }
1✔
192

193
    @Override
194
    public RulePriority getPriority() {
195
        return priority;
1✔
196
    }
197

198
    @Override
199
    public void setPriority(RulePriority priority) {
200
        this.priority = priority;
1✔
201
    }
1✔
202

203

204
    private Set<Class<? extends Node>> getClassRuleChainVisits() {
205
        if (classRuleChainVisits.isEmpty() && ruleChainVisits.isEmpty()) {
1!
206
            return Collections.singleton(RootNode.class);
1✔
207
        }
208
        return classRuleChainVisits;
×
209
    }
210

211
    @Override
212
    public final RuleTargetSelector getTargetSelector() {
213
        if (myStrategy == null) {
1✔
214
            myStrategy = buildTargetSelector();
1✔
215
        }
216
        return myStrategy;
1✔
217
    }
218

219
    /**
220
     * Create the targeting strategy for this rule.
221
     * Use the factory methods of {@link RuleTargetSelector}.
222
     */
223
    protected @NonNull RuleTargetSelector buildTargetSelector() {
224
        Set<Class<? extends Node>> crvs = getClassRuleChainVisits();
1✔
225
        return crvs.isEmpty() ? RuleTargetSelector.forRootOnly()
1!
226
                              : RuleTargetSelector.forTypes(crvs);
1✔
227
    }
228

229
    @Override
230
    public void start(RuleContext ctx) {
231
        // Override as needed
232
    }
1✔
233

234
    @Override
235
    public void end(RuleContext ctx) {
236
        // Override as needed
237
    }
1✔
238

239
    // TODO remove those methods, make Rules have type-safe access to a RuleContext
240

241
    /**
242
     * Cast the argument to a {@link RuleContext}. Use it to report violations:
243
     * <pre>{@code
244
     *  asCtx(data).addViolation(node);
245
     *  asCtx(data).addViolationWithMessage(node, "Some message");
246
     * }</pre>
247
     *
248
     * In longer term, rules will have type-safe access to a RuleContext, when the
249
     * rules use an appropriate visitor. Many rules have not been refactored yet.
250
     * Once this is done, this method will be deprecated as useless. Until then,
251
     * this is a way to hide the explicit cast to {@link RuleContext} in rules.
252
     */
253
    protected final RuleContext asCtx(Object ctx) {
254
        if (ctx instanceof RuleContext) {
×
255
            assert isThisRule(InternalApiBridge.getRule((RuleContext) ctx))
×
256
                : "not an appropriate rule context!";
257
            return (RuleContext) ctx;
×
258
        } else {
259
            throw new ClassCastException("Unexpected context object! " + ctx);
×
260
        }
261
    }
262

263
    private boolean isThisRule(Rule rule) {
264
        return rule == this // NOPMD CompareObjectsWithEquals
×
265
            || rule instanceof RuleReference && this.isThisRule(((RuleReference) rule).getRule());
×
266
    }
267

268
    /**
269
     * Rules are equal if:
270
     * <ol>
271
     * <li>They have the same implementation class.</li>
272
     * <li>They have the same name.</li>
273
     * <li>They have the same priority.</li>
274
     * <li>They share the same properties.</li>
275
     * </ol>
276
     */
277
    @Override
278
    public boolean equals(Object o) {
279
        if (this == o) {
1✔
280
            return true; // trivial
1✔
281
        }
282
        if (o == null || getClass() != o.getClass()) {
1✔
283
            return false;
1✔
284
        }
285

286
        AbstractRule that = (AbstractRule) o;
1✔
287
        return Objects.equals(getName(), that.getName())
1✔
288
                && Objects.equals(getPriority(), that.getPriority())
1✔
289
                && super.equals(o);
1✔
290
    }
291

292
    @Override
293
    public int hashCode() {
294
        return Objects.hash(getName(), getPriority(), super.hashCode());
1✔
295
    }
296

297
    @SuppressWarnings("unchecked")
298
    @Override
299
    public Rule deepCopy() {
300
        Rule result;
301
        try {
302
            Constructor<? extends AbstractRule> declaredConstructor = getClass().getDeclaredConstructor();
1✔
303
            declaredConstructor.setAccessible(true);
1✔
304
            result = declaredConstructor.newInstance();
1✔
305
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ignored) {
×
306
            // Can't happen... we already have an instance
307
            throw new RuntimeException(ignored); // in case it happens anyway, something is really wrong...
×
308
        }
1✔
309
        Rule rule = result;
1✔
310
        rule.setName(getName());
1✔
311
        rule.setLanguage(getLanguage());
1✔
312
        rule.setMinimumLanguageVersion(getMinimumLanguageVersion());
1✔
313
        rule.setMaximumLanguageVersion(getMaximumLanguageVersion());
1✔
314
        rule.setSince(getSince());
1✔
315
        rule.setMessage(getMessage());
1✔
316
        rule.setRuleSetName(getRuleSetName());
1✔
317
        rule.setExternalInfoUrl(getExternalInfoUrl());
1✔
318
        rule.setDescription(getDescription());
1✔
319
        for (final String example : getExamples()) {
1✔
320
            rule.addExample(example);
1✔
321
        }
1✔
322
        rule.setPriority(getPriority());
1✔
323
        for (final PropertyDescriptor<?> prop : getPropertyDescriptors()) {
1✔
324
            // define the descriptor only if it doesn't yet exist
325
            if (rule.getPropertyDescriptor(prop.name()) == null) {
1✔
326
                rule.definePropertyDescriptor(prop); // Property descriptors are immutable, and can be freely shared
1✔
327
            }
328

329
            if (isPropertyOverridden(prop)) {
1✔
330
                rule.setProperty((PropertyDescriptor<Object>) prop, getProperty((PropertyDescriptor<Object>) prop));
1✔
331
            }
332
        }
1✔
333
        return rule;
1✔
334
    }
335

336
    @Override
337
    @SuppressWarnings("PMD.UselessOverridingMethod")
338
    public String dysfunctionReason() {
339
        // This method is overridden to let subclasses remove their implementation
340
        // of dysfunctionReason in a binary compatible way. For this to
341
        // work one implementation has to be in a superclass, however this
342
        // method only has a default implementation in the interface.
343
        return super.dysfunctionReason();
1✔
344
    }
345
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc