• 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.87
/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSetWriter.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.io.OutputStream;
8
import java.util.HashSet;
9
import java.util.List;
10
import java.util.Map;
11
import java.util.Set;
12
import java.util.regex.Pattern;
13
import javax.xml.parsers.DocumentBuilder;
14
import javax.xml.parsers.DocumentBuilderFactory;
15
import javax.xml.parsers.FactoryConfigurationError;
16
import javax.xml.parsers.ParserConfigurationException;
17
import javax.xml.transform.OutputKeys;
18
import javax.xml.transform.Transformer;
19
import javax.xml.transform.TransformerException;
20
import javax.xml.transform.TransformerFactory;
21
import javax.xml.transform.dom.DOMSource;
22
import javax.xml.transform.stream.StreamResult;
23

24
import org.checkerframework.checker.nullness.qual.NonNull;
25
import org.checkerframework.checker.nullness.qual.Nullable;
26
import org.slf4j.Logger;
27
import org.slf4j.LoggerFactory;
28
import org.w3c.dom.CDATASection;
29
import org.w3c.dom.DOMException;
30
import org.w3c.dom.Document;
31
import org.w3c.dom.Element;
32
import org.w3c.dom.Text;
33

34
import net.sourceforge.pmd.internal.util.IOUtil;
35
import net.sourceforge.pmd.lang.Language;
36
import net.sourceforge.pmd.lang.LanguageVersion;
37
import net.sourceforge.pmd.lang.rule.internal.RuleSetReference;
38
import net.sourceforge.pmd.properties.InternalApiBridge;
39
import net.sourceforge.pmd.properties.PropertyConstraint;
40
import net.sourceforge.pmd.properties.PropertyDescriptor;
41
import net.sourceforge.pmd.properties.PropertySerializer;
42
import net.sourceforge.pmd.properties.PropertySource;
43
import net.sourceforge.pmd.properties.internal.PropertyTypeId;
44
import net.sourceforge.pmd.util.internal.xml.SchemaConstants;
45

46
/**
47
 * This class represents a way to serialize a RuleSet to an XML configuration
48
 * file.
49
 */
50
public class RuleSetWriter {
51
    private static final Logger LOG = LoggerFactory.getLogger(RuleSetWriter.class);
1✔
52

53
    public static final String RULESET_2_0_0_NS_URI = "http://pmd.sourceforge.net/ruleset/2.0.0";
54

55
    private final OutputStream outputStream;
56
    private Document document;
57
    private Set<String> ruleSetFileNames;
58

59
    public RuleSetWriter(OutputStream outputStream) {
1✔
60
        this.outputStream = outputStream;
1✔
61
    }
1✔
62

63
    public void close() {
64
        IOUtil.closeQuietly(outputStream);
1✔
65
    }
1✔
66

67
    public void write(RuleSet ruleSet) {
68
        try {
69
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
1✔
70
            documentBuilderFactory.setNamespaceAware(true);
1✔
71
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
1✔
72
            document = documentBuilder.newDocument();
1✔
73
            ruleSetFileNames = new HashSet<>();
1✔
74

75
            Element ruleSetElement = createRuleSetElement(ruleSet);
1✔
76
            document.appendChild(ruleSetElement);
1✔
77

78
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
1✔
79
            try {
80
                transformerFactory.setAttribute("indent-number", 3);
×
81
            } catch (IllegalArgumentException iae) {
1✔
82
                // ignore it, specific to one parser
83
                LOG.debug("Couldn't set indentation", iae);
1✔
84
            }
×
85
            Transformer transformer = transformerFactory.newTransformer();
1✔
86
            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
1✔
87
            // This is as close to pretty printing as we'll get using standard
88
            // Java APIs.
89
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
1✔
90
            transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
1✔
91
            transformer.transform(new DOMSource(document), new StreamResult(outputStream));
1✔
92
        } catch (DOMException | FactoryConfigurationError | ParserConfigurationException | TransformerException e) {
×
93
            throw new RuntimeException(e);
×
94
        }
1✔
95
    }
1✔
96

97
    private Element createRuleSetElement(RuleSet ruleSet) {
98
        Element ruleSetElement = document.createElementNS(RULESET_2_0_0_NS_URI, "ruleset");
1✔
99
        ruleSetElement.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1✔
100
        ruleSetElement.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation",
1✔
101
                RULESET_2_0_0_NS_URI + " https://pmd.sourceforge.io/ruleset_2_0_0.xsd");
102
        ruleSetElement.setAttribute("name", ruleSet.getName());
1✔
103

104
        Element descriptionElement = createDescriptionElement(ruleSet.getDescription());
1✔
105
        ruleSetElement.appendChild(descriptionElement);
1✔
106

107
        for (Pattern excludePattern : ruleSet.getFileExclusions()) {
1!
108
            Element excludePatternElement = createExcludePatternElement(excludePattern.pattern());
×
109
            ruleSetElement.appendChild(excludePatternElement);
×
110
        }
×
111
        for (Pattern includePattern : ruleSet.getFileInclusions()) {
1!
112
            Element includePatternElement = createIncludePatternElement(includePattern.pattern());
×
113
            ruleSetElement.appendChild(includePatternElement);
×
114
        }
×
115
        for (Rule rule : ruleSet.getRules()) {
1✔
116
            Element ruleElement = createRuleElement(rule);
1✔
117
            if (ruleElement != null) {
1✔
118
                ruleSetElement.appendChild(ruleElement);
1✔
119
            }
120
        }
1✔
121

122
        return ruleSetElement;
1✔
123
    }
124

125
    private Element createDescriptionElement(String description) {
126
        return createTextElement("description", description);
1✔
127
    }
128

129
    private Element createPropertyValueElement(String name) {
130
        return document.createElementNS(RULESET_2_0_0_NS_URI, name);
1✔
131
    }
132

133
    private Element createExcludePatternElement(String excludePattern) {
134
        return createTextElement("exclude-pattern", excludePattern);
×
135
    }
136

137
    private Element createIncludePatternElement(String includePattern) {
138
        return createTextElement("include-pattern", includePattern);
×
139
    }
140

141
    private Element createRuleElement() {
142
        return document.createElementNS(RULESET_2_0_0_NS_URI, "rule");
1✔
143
    }
144

145
    private Element createExcludeElement(String exclude) {
146
        Element element = document.createElementNS(RULESET_2_0_0_NS_URI, "exclude");
1✔
147
        element.setAttribute("name", exclude);
1✔
148
        return element;
1✔
149
    }
150

151
    private Element createExampleElement(String example) {
152
        return createCDATASectionElement("example", example);
×
153
    }
154

155
    private Element createPriorityElement(RulePriority priority) {
156
        return createTextElement("priority", String.valueOf(priority.getPriority()));
1✔
157
    }
158

159
    private Element createPropertiesElement() {
160
        return document.createElementNS(RULESET_2_0_0_NS_URI, "properties");
1✔
161
    }
162

163
    private Element createRuleElement(Rule rule) {
164
        if (rule instanceof RuleReference) {
1✔
165
            RuleReference ruleReference = (RuleReference) rule;
1✔
166
            RuleSetReference ruleSetReference = ruleReference.getRuleSetReference();
1✔
167
            if (ruleSetReference.isAllRules()) {
1✔
168
                if (!ruleSetFileNames.contains(ruleSetReference.getRuleSetFileName())) {
1✔
169
                    ruleSetFileNames.add(ruleSetReference.getRuleSetFileName());
1✔
170
                    return createRuleSetReferenceElement(ruleSetReference);
1✔
171
                } else {
172
                    return null;
1✔
173
                }
174
            } else {
175
                LanguageVersion minimumLanguageVersion = ruleReference.getOverriddenMinimumLanguageVersion();
1✔
176
                LanguageVersion maximumLanguageVersion = ruleReference.getOverriddenMaximumLanguageVersion();
1✔
177
                Boolean deprecated = ruleReference.isOverriddenDeprecated();
1✔
178
                String name = ruleReference.getOverriddenName();
1✔
179
                String ref = ruleReference.getRuleSetReference().getRuleSetFileName() + '/'
1✔
180
                        + ruleReference.getRule().getName();
1✔
181
                String message = ruleReference.getOverriddenMessage();
1✔
182
                String externalInfoUrl = ruleReference.getOverriddenExternalInfoUrl();
1✔
183
                String description = ruleReference.getOverriddenDescription();
1✔
184
                RulePriority priority = ruleReference.getOverriddenPriority();
1✔
185
                List<String> examples = ruleReference.getOverriddenExamples();
1✔
186

187
                return createSingleRuleElement(null, minimumLanguageVersion, maximumLanguageVersion, deprecated,
1✔
188
                                               name, null, ref, message, externalInfoUrl, null, description, priority,
189
                                               ruleReference, examples);
190
            }
191
        } else {
192
            return createSingleRuleElement(rule.getLanguage(),
1✔
193
                                           rule.getMinimumLanguageVersion(), rule.getMaximumLanguageVersion(), rule.isDeprecated(),
1✔
194
                                           rule.getName(), rule.getSince(), null, rule.getMessage(), rule.getExternalInfoUrl(),
1✔
195
                                           rule.getRuleClass(),
1✔
196
                                           rule.getDescription(),
1✔
197
                                           rule.getPriority(), rule,
1✔
198
                                           rule.getExamples());
1✔
199
        }
200
    }
201

202
    private void setIfNonNull(Object value, Element target, String id) {
203
        if (value != null) {
1✔
204
            target.setAttribute(id, value.toString());
1✔
205
        }
206
    }
1✔
207

208
    private Element createSingleRuleElement(Language language, LanguageVersion minimumLanguageVersion,
209
                                            LanguageVersion maximumLanguageVersion, Boolean deprecated, String name, String since, String ref,
210
                                            String message, String externalInfoUrl, String clazz,
211
                                            String description, RulePriority priority, PropertySource propertySource, List<String> examples) {
212
        Element ruleElement = createRuleElement();
1✔
213
        // language is now a required attribute, unless this is a rule reference
214
        if (clazz != null) {
1✔
215
            ruleElement.setAttribute("language", language.getId());
1✔
216
        }
217
        if (minimumLanguageVersion != null) {
1!
218
            ruleElement.setAttribute("minimumLanguageVersion", minimumLanguageVersion.getVersion());
×
219
        }
220
        if (maximumLanguageVersion != null) {
1!
221
            ruleElement.setAttribute("maximumLanguageVersion", maximumLanguageVersion.getVersion());
×
222
        }
223

224
        setIfNonNull(deprecated, ruleElement, "deprecated");
1✔
225
        setIfNonNull(name, ruleElement, "name");
1✔
226
        setIfNonNull(since, ruleElement, "since");
1✔
227
        setIfNonNull(ref, ruleElement, "ref");
1✔
228
        setIfNonNull(message, ruleElement, "message");
1✔
229
        setIfNonNull(clazz, ruleElement, "class");
1✔
230
        setIfNonNull(externalInfoUrl, ruleElement, "externalInfoUrl");
1✔
231

232
        if (description != null) {
1!
233
            Element descriptionElement = createDescriptionElement(description);
×
234
            ruleElement.appendChild(descriptionElement);
×
235
        }
236
        if (priority != null) {
1✔
237
            Element priorityElement = createPriorityElement(priority);
1✔
238
            ruleElement.appendChild(priorityElement);
1✔
239
        }
240
        Element propertiesElement = createPropertiesElement(propertySource);
1✔
241
        if (propertiesElement != null) {
1✔
242
            ruleElement.appendChild(propertiesElement);
1✔
243
        }
244
        if (examples != null) {
1✔
245
            for (String example : examples) {
1!
246
                Element exampleElement = createExampleElement(example);
×
247
                ruleElement.appendChild(exampleElement);
×
248
            }
×
249
        }
250
        return ruleElement;
1✔
251
    }
252

253
    private Element createRuleSetReferenceElement(RuleSetReference ruleSetReference) {
254
        Element ruleSetReferenceElement = createRuleElement();
1✔
255
        ruleSetReferenceElement.setAttribute("ref", ruleSetReference.getRuleSetFileName());
1✔
256
        for (String exclude : ruleSetReference.getExcludes()) {
1✔
257
            Element excludeElement = createExcludeElement(exclude);
1✔
258
            ruleSetReferenceElement.appendChild(excludeElement);
1✔
259
        }
1✔
260
        return ruleSetReferenceElement;
1✔
261
    }
262

263
    private @Nullable Element createPropertiesElement(PropertySource propertySource) {
264

265
        Element propertiesElement = null;
1✔
266
        List<PropertyDescriptor<?>> overridden = propertySource.getOverriddenPropertyDescriptors();
1✔
267
        List<PropertyDescriptor<?>> defined = propertySource.getPropertyDescriptors();
1✔
268

269
        for (PropertyDescriptor<?> descriptor : defined) {
1✔
270
            // For each provided PropertyDescriptor
271

272
            PropertyTypeId typeId = InternalApiBridge.getTypeId(descriptor);
1✔
273
            // RuleReferences can't define additional properties
274
            boolean isPropertyDefinition = typeId != null && !(propertySource instanceof RuleReference);
1✔
275

276
            // skip properties, which neither are definitions nor override the default value
277
            if (!isPropertyDefinition && !overridden.contains(descriptor)) {
1✔
278
                continue;
1✔
279
            }
280

281
            if (propertiesElement == null) {
1✔
282
                propertiesElement = createPropertiesElement();
1✔
283
            }
284

285
            if (isPropertyDefinition) {
1✔
286
                propertiesElement.appendChild(createPropertyDefinitionElementBR(descriptor, typeId));
1✔
287
            } else {
288
                propertiesElement.appendChild(propertyElementWithValueAttribute(propertySource, descriptor));
1✔
289
            }
290
        }
1✔
291

292
        return propertiesElement;
1✔
293
    }
294

295
    @NonNull
296
    private <T> Element propertyElementWithValueAttribute(PropertySource propertySource, PropertyDescriptor<T> propertyDescriptor) {
297
        Element element = document.createElementNS(RULESET_2_0_0_NS_URI, "property");
1✔
298
        SchemaConstants.NAME.setOn(element, propertyDescriptor.name());
1✔
299

300
        PropertySerializer<T> xmlStrategy = propertyDescriptor.serializer();
1✔
301
        T value = propertySource.getProperty(propertyDescriptor);
1✔
302
        SchemaConstants.PROPERTY_VALUE.setOn(element, xmlStrategy.toString(value));
1✔
303
        return element;
1✔
304
    }
305

306
    private <T> Element createPropertyValueElement(PropertyDescriptor<T> propertyDescriptor, T value) {
307
        Element element = document.createElementNS(RULESET_2_0_0_NS_URI, "property");
1✔
308
        SchemaConstants.NAME.setOn(element, propertyDescriptor.name());
1✔
309

310
        PropertySerializer<T> xmlStrategy = propertyDescriptor.serializer();
1✔
311

312
        Element valueElt = createPropertyValueElement(SchemaConstants.PROPERTY_VALUE.xmlName());
1✔
313
        valueElt.setTextContent(xmlStrategy.toString(value));
1✔
314
        element.appendChild(valueElt);
1✔
315

316
        return element;
1✔
317
    }
318

319
    private <T> Element createPropertyDefinitionElementBR(PropertyDescriptor<T> propertyDescriptor, @NonNull PropertyTypeId typeId) {
320

321
        final Element element = createPropertyValueElement(propertyDescriptor, propertyDescriptor.defaultValue());
1✔
322

323
        SchemaConstants.NAME.setOn(element, propertyDescriptor.name());
1✔
324
        SchemaConstants.PROPERTY_TYPE.setOn(element, typeId.getStringId());
1✔
325
        SchemaConstants.DESCRIPTION.setOn(element, propertyDescriptor.description());
1✔
326

327
        for (PropertyConstraint<? super T> constraint : propertyDescriptor.serializer().getConstraints()) {
1✔
328
            Map<String, String> attributes = constraint.getXmlConstraint();
1✔
329

330
            if (attributes == null || attributes.isEmpty()) {
1!
331
                throw new IllegalArgumentException("Unsupported property constraint in XML: " + constraint);
×
332
            }
333

334
            for (Map.Entry<String, String> attribute : attributes.entrySet()) {
1✔
335
                if (SchemaConstants.PROPERTY_MAX.xmlName().equals(attribute.getKey())) {
1✔
336
                    SchemaConstants.PROPERTY_MAX.setOn(element, attribute.getValue());
1✔
337
                } else if (SchemaConstants.PROPERTY_MIN.xmlName().equals(attribute.getKey())) {
1!
338
                    SchemaConstants.PROPERTY_MIN.setOn(element, attribute.getValue());
1✔
339
                } else {
340
                    throw new IllegalArgumentException("Unsupported property constraint in XML: " + constraint
×
341
                            + ". There is no attribute " + attribute.getKey());
×
342
                }
343
            }
1✔
344

345
        }
1✔
346

347
        return element;
1✔
348
    }
349

350
    private Element createTextElement(String name, String value) {
351
        Element element = document.createElementNS(RULESET_2_0_0_NS_URI, name);
1✔
352
        Text text = document.createTextNode(value);
1✔
353
        element.appendChild(text);
1✔
354
        return element;
1✔
355
    }
356

357
    private Element createCDATASectionElement(String name, String value) {
358
        Element element = document.createElementNS(RULESET_2_0_0_NS_URI, name);
×
359
        CDATASection cdataSection = document.createCDATASection(value);
×
360
        element.appendChild(cdataSection);
×
361
        return element;
×
362
    }
363
}
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