1
2
3
4 package net.sourceforge.pmd;
5
6 import java.io.OutputStream;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import javax.xml.parsers.DocumentBuilder;
13 import javax.xml.parsers.DocumentBuilderFactory;
14 import javax.xml.parsers.FactoryConfigurationError;
15 import javax.xml.parsers.ParserConfigurationException;
16 import javax.xml.transform.OutputKeys;
17 import javax.xml.transform.Transformer;
18 import javax.xml.transform.TransformerException;
19 import javax.xml.transform.TransformerFactory;
20 import javax.xml.transform.dom.DOMSource;
21 import javax.xml.transform.stream.StreamResult;
22
23 import net.sourceforge.pmd.lang.Language;
24 import net.sourceforge.pmd.lang.LanguageVersion;
25 import net.sourceforge.pmd.lang.rule.ImmutableLanguage;
26 import net.sourceforge.pmd.lang.rule.RuleReference;
27 import net.sourceforge.pmd.lang.rule.XPathRule;
28 import net.sourceforge.pmd.lang.rule.properties.PropertyDescriptorWrapper;
29 import net.sourceforge.pmd.lang.rule.properties.factories.PropertyDescriptorUtil;
30
31 import org.apache.commons.io.IOUtils;
32 import org.w3c.dom.CDATASection;
33 import org.w3c.dom.DOMException;
34 import org.w3c.dom.Document;
35 import org.w3c.dom.Element;
36 import org.w3c.dom.Text;
37
38
39
40
41 public class RuleSetWriter {
42
43 public static final String RULESET_NS_URI = "http://pmd.sourceforge.net/ruleset/2.0.0";
44
45 private final OutputStream outputStream;
46 private Document document;
47 private Set<String> ruleSetFileNames;
48
49 public RuleSetWriter(OutputStream outputStream) {
50 this.outputStream = outputStream;
51 }
52
53 public void close() {
54 IOUtils.closeQuietly(outputStream);
55 }
56
57 public void write(RuleSet ruleSet) {
58 try {
59 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
60 documentBuilderFactory.setNamespaceAware(true);
61 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
62 document = documentBuilder.newDocument();
63 ruleSetFileNames = new HashSet<String>();
64
65 Element ruleSetElement = createRuleSetElement(ruleSet);
66 document.appendChild(ruleSetElement);
67
68 TransformerFactory transformerFactory = TransformerFactory.newInstance();
69 try {
70 transformerFactory.setAttribute("indent-number", 3);
71 } catch (IllegalArgumentException iae) {
72
73 }
74 Transformer transformer = transformerFactory.newTransformer();
75 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
76
77 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
78 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
79 transformer.transform(new DOMSource(document), new StreamResult(outputStream));
80 } catch (DOMException e) {
81 throw new RuntimeException(e);
82 } catch (FactoryConfigurationError e) {
83 throw new RuntimeException(e);
84 } catch (ParserConfigurationException e) {
85 throw new RuntimeException(e);
86 } catch (TransformerException e) {
87 throw new RuntimeException(e);
88 }
89 }
90
91 private Element createRuleSetElement(RuleSet ruleSet) {
92 Element ruleSetElement = document.createElementNS(RULESET_NS_URI, "ruleset");
93 ruleSetElement.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
94 ruleSetElement.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation", RULESET_NS_URI + " http://pmd.sourceforge.net/ruleset_2_0_0.xsd");
95 ruleSetElement.setAttribute("name", ruleSet.getName());
96
97 Element descriptionElement = createDescriptionElement(ruleSet.getDescription());
98 ruleSetElement.appendChild(descriptionElement);
99
100 for (String excludePattern : ruleSet.getExcludePatterns()) {
101 Element excludePatternElement = createExcludePatternElement(excludePattern);
102 ruleSetElement.appendChild(excludePatternElement);
103 }
104 for (String includePattern : ruleSet.getIncludePatterns()) {
105 Element includePatternElement = createIncludePatternElement(includePattern);
106 ruleSetElement.appendChild(includePatternElement);
107 }
108 for (Rule rule : ruleSet.getRules()) {
109 Element ruleElement = createRuleElement(rule);
110 if (ruleElement != null) {
111 ruleSetElement.appendChild(ruleElement);
112 }
113 }
114
115 return ruleSetElement;
116 }
117
118 private Element createDescriptionElement(String description) {
119 return createTextElement("description", description);
120 }
121
122 private Element createExcludePatternElement(String excludePattern) {
123 return createTextElement("exclude-pattern", excludePattern);
124 }
125
126 private Element createIncludePatternElement(String includePattern) {
127 return createTextElement("include-pattern", includePattern);
128 }
129
130 private Element createRuleElement() {
131 return document.createElementNS(RULESET_NS_URI, "rule");
132 }
133
134 private Element createExcludeElement(String exclude) {
135 Element element = document.createElementNS(RULESET_NS_URI, "exclude");
136 element.setAttribute("name", exclude);
137 return element;
138 }
139
140 private Element createExampleElement(String example) {
141 return createCDATASectionElement("example", example);
142 }
143
144 private Element createPriorityElement(RulePriority priority) {
145 return createTextElement("priority", String.valueOf(priority.getPriority()));
146 }
147
148 private Element createPropertiesElement() {
149 return document.createElementNS(RULESET_NS_URI, "properties");
150 }
151
152 private Element createRuleElement(Rule rule) {
153 if (rule instanceof RuleReference) {
154 RuleReference ruleReference = (RuleReference) rule;
155 RuleSetReference ruleSetReference = ruleReference.getRuleSetReference();
156 if (ruleSetReference.isAllRules()) {
157 if (!ruleSetFileNames.contains(ruleSetReference.getRuleSetFileName())) {
158 ruleSetFileNames.add(ruleSetReference.getRuleSetFileName());
159 Element ruleSetReferenceElement = createRuleSetReferenceElement(ruleSetReference);
160 return ruleSetReferenceElement;
161 } else {
162 return null;
163 }
164 } else {
165 Language language = ruleReference.getOverriddenLanguage();
166 LanguageVersion minimumLanguageVersion = ruleReference.getOverriddenMinimumLanguageVersion();
167 LanguageVersion maximumLanguageVersion = ruleReference.getOverriddenMaximumLanguageVersion();
168 Boolean deprecated = ruleReference.isOverriddenDeprecated();
169 String name = ruleReference.getOverriddenName();
170 String ref = ruleReference.getRuleSetReference().getRuleSetFileName() + "/" + ruleReference.getName();
171 String message = ruleReference.getOverriddenMessage();
172 String externalInfoUrl = ruleReference.getOverriddenExternalInfoUrl();
173 String description = ruleReference.getOverriddenDescription();
174 RulePriority priority = ruleReference.getOverriddenPriority();
175 List<PropertyDescriptor<?>> propertyDescriptors = ruleReference.getOverriddenPropertyDescriptors();
176 Map<PropertyDescriptor<?>, Object> propertiesByPropertyDescriptor = ruleReference.getOverriddenPropertiesByPropertyDescriptor();
177 List<String> examples = ruleReference.getOverriddenExamples();
178
179 return createSingleRuleElement(language, minimumLanguageVersion, maximumLanguageVersion, deprecated,
180 name, null, ref, message, externalInfoUrl, null, null, null, description, priority,
181 propertyDescriptors, propertiesByPropertyDescriptor, examples);
182 }
183 } else {
184 return createSingleRuleElement(rule instanceof ImmutableLanguage ? null : rule.getLanguage(),
185 rule.getMinimumLanguageVersion(), rule.getMaximumLanguageVersion(), rule.isDeprecated(),
186 rule.getName(), rule.getSince(), null, rule.getMessage(), rule.getExternalInfoUrl(),
187 rule.getRuleClass(), rule.usesDFA(), rule.usesTypeResolution(), rule.getDescription(),
188 rule.getPriority(), rule.getPropertyDescriptors(), rule.getPropertiesByPropertyDescriptor(),
189 rule.getExamples());
190 }
191 }
192
193 private void setIfNonNull(Object value, Element target, String id) {
194 if (value != null) {
195 target.setAttribute(id, value.toString());
196 }
197 }
198
199 private Element createSingleRuleElement(Language language, LanguageVersion minimumLanguageVersion,
200 LanguageVersion maximumLanguageVersion, Boolean deprecated, String name, String since, String ref,
201 String message, String externalInfoUrl, String clazz, Boolean dfa, Boolean typeResolution,
202 String description, RulePriority priority, List<PropertyDescriptor<?>> propertyDescriptors,
203 Map<PropertyDescriptor<?>, Object> propertiesByPropertyDescriptor, List<String> examples) {
204 Element ruleElement = createRuleElement();
205 if (language != null) {
206 ruleElement.setAttribute("language", language.getTerseName());
207 }
208 if (minimumLanguageVersion != null) {
209 ruleElement.setAttribute("minimumLanguageVersion", minimumLanguageVersion.getVersion());
210 }
211 if (maximumLanguageVersion != null) {
212 ruleElement.setAttribute("maximumLanguageVersion", maximumLanguageVersion.getVersion());
213 }
214
215 setIfNonNull(deprecated, ruleElement, "deprecated");
216 setIfNonNull(name, ruleElement, "name");
217 setIfNonNull(since, ruleElement, "since");
218 setIfNonNull(ref, ruleElement, "ref");
219 setIfNonNull(message, ruleElement, "message");
220 setIfNonNull(clazz, ruleElement, "class");
221 setIfNonNull(externalInfoUrl, ruleElement, "externalInfoUrl");
222 setIfNonNull(dfa, ruleElement, "dfa");
223 setIfNonNull(typeResolution, ruleElement, "typeResolution");
224
225 if (description != null) {
226 Element descriptionElement = createDescriptionElement(description);
227 ruleElement.appendChild(descriptionElement);
228 }
229 if (priority != null) {
230 Element priorityElement = createPriorityElement(priority);
231 ruleElement.appendChild(priorityElement);
232 }
233 Element propertiesElement = createPropertiesElement(propertyDescriptors, propertiesByPropertyDescriptor);
234 if (propertiesElement != null) {
235 ruleElement.appendChild(propertiesElement);
236 }
237 if (examples != null) {
238 for (String example : examples) {
239 Element exampleElement = createExampleElement(example);
240 ruleElement.appendChild(exampleElement);
241 }
242 }
243 return ruleElement;
244 }
245
246 private Element createRuleSetReferenceElement(RuleSetReference ruleSetReference) {
247 Element ruleSetReferenceElement = createRuleElement();
248 ruleSetReferenceElement.setAttribute("ref", ruleSetReference.getRuleSetFileName());
249 for (String exclude : ruleSetReference.getExcludes()) {
250 Element excludeElement = createExcludeElement(exclude);
251 ruleSetReferenceElement.appendChild(excludeElement);
252 }
253 return ruleSetReferenceElement;
254 }
255
256 @SuppressWarnings("PMD.CompareObjectsWithEquals")
257 private Element createPropertiesElement(List<PropertyDescriptor<?>> propertyDescriptors, Map<PropertyDescriptor<?>, Object> propertiesByPropertyDescriptor) {
258
259 Element propertiesElement = null;
260 if (propertyDescriptors != null) {
261
262 for (PropertyDescriptor<?> propertyDescriptor : propertyDescriptors) {
263
264 if (propertyDescriptor instanceof PropertyDescriptorWrapper) {
265 if (propertiesElement == null) {
266 propertiesElement = createPropertiesElement();
267 }
268
269 Element propertyElement = createPropertyDefinitionElementBR(((PropertyDescriptorWrapper<?>) propertyDescriptor).getPropertyDescriptor());
270 propertiesElement.appendChild(propertyElement);
271 } else {
272 if (propertiesByPropertyDescriptor != null) {
273 Object defaultValue = propertyDescriptor.defaultValue();
274 Object value = propertiesByPropertyDescriptor.get(propertyDescriptor);
275 if (value != defaultValue && (value == null || !value.equals(defaultValue))) {
276 if (propertiesElement == null) {
277 propertiesElement = createPropertiesElement();
278 }
279
280 Element propertyElement = createPropertyValueElement(propertyDescriptor, value);
281 propertiesElement.appendChild(propertyElement);
282 }
283 }
284 }
285 }
286 }
287
288 if (propertiesByPropertyDescriptor != null) {
289
290 for (Map.Entry<PropertyDescriptor<?>, Object> entry : propertiesByPropertyDescriptor.entrySet()) {
291
292 PropertyDescriptor<?> propertyDescriptor = entry.getKey();
293 if (!propertyDescriptors.contains(propertyDescriptor)) {
294
295
296 Object defaultValue = propertyDescriptor.defaultValue();
297 Object value = entry.getValue();
298 if (value != defaultValue && (value == null || !value.equals(defaultValue))) {
299 if (propertiesElement == null) {
300 propertiesElement = createPropertiesElement();
301 }
302 Element propertyElement = createPropertyValueElement(propertyDescriptor, value);
303 propertiesElement.appendChild(propertyElement);
304 }
305 }
306 }
307 }
308 return propertiesElement;
309 }
310
311 private Element createPropertyValueElement(PropertyDescriptor propertyDescriptor, Object value) {
312 Element propertyElement = document.createElementNS(RULESET_NS_URI, "property");
313 propertyElement.setAttribute("name", propertyDescriptor.name());
314 String valueString = propertyDescriptor.asDelimitedString(value);
315 if (XPathRule.XPATH_DESCRIPTOR.equals(propertyDescriptor)) {
316 Element valueElement = createCDATASectionElement("value", valueString);
317 propertyElement.appendChild(valueElement);
318 } else {
319 propertyElement.setAttribute("value", valueString);
320 }
321
322 return propertyElement;
323 }
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344 private Element createPropertyDefinitionElementBR(PropertyDescriptor<?> propertyDescriptor) {
345
346 final Element propertyElement = createPropertyValueElement(propertyDescriptor, propertyDescriptor.defaultValue());
347 propertyElement.setAttribute(PropertyDescriptorFields.TYPE, PropertyDescriptorUtil.typeIdFor(propertyDescriptor.type()));
348
349 Map<String, String> propertyValuesById = propertyDescriptor.attributeValuesById();
350 for (Map.Entry<String, String> entry : propertyValuesById.entrySet()) {
351 propertyElement.setAttribute(entry.getKey(), entry.getValue());
352 }
353
354 return propertyElement;
355 }
356
357 private Element createTextElement(String name, String value) {
358 Element element = document.createElementNS(RULESET_NS_URI, name);
359 Text text = document.createTextNode(value);
360 element.appendChild(text);
361 return element;
362 }
363
364 private Element createCDATASectionElement(String name, String value) {
365 Element element = document.createElementNS(RULESET_NS_URI, name);
366 CDATASection cdataSection = document.createCDATASection(value);
367 element.appendChild(cdataSection);
368 return element;
369 }
370 }