1 package net.sourceforge.pmd;
2
3 import java.io.IOException;
4 import java.io.OutputStream;
5 import java.util.HashSet;
6 import java.util.List;
7 import java.util.Map;
8 import java.util.Properties;
9 import java.util.Set;
10
11 import javax.xml.parsers.DocumentBuilder;
12 import javax.xml.parsers.DocumentBuilderFactory;
13 import javax.xml.parsers.FactoryConfigurationError;
14 import javax.xml.parsers.ParserConfigurationException;
15 import javax.xml.transform.OutputKeys;
16 import javax.xml.transform.Transformer;
17 import javax.xml.transform.TransformerException;
18 import javax.xml.transform.TransformerFactory;
19 import javax.xml.transform.dom.DOMSource;
20 import javax.xml.transform.stream.StreamResult;
21
22 import org.w3c.dom.CDATASection;
23 import org.w3c.dom.DOMException;
24 import org.w3c.dom.Document;
25 import org.w3c.dom.Element;
26 import org.w3c.dom.Text;
27
28 /**
29 * This class represents a way to serialize a RuleSet to an XML configuration file.
30 */
31 public class RuleSetWriter {
32 private final OutputStream outputStream;
33 private final boolean outputNamespace;
34 private Document document;
35 private Set<String> ruleSetFileNames;
36
37 public RuleSetWriter(OutputStream outputStream) {
38 this(outputStream, true);
39 }
40
41 public RuleSetWriter(OutputStream outputStream, boolean outputNamespace) {
42 this.outputStream = outputStream;
43 this.outputNamespace = outputNamespace;
44 }
45
46 public void close() throws IOException {
47 outputStream.flush();
48 outputStream.close();
49 }
50
51 public void write(RuleSet ruleSet) {
52 try {
53 DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
54 DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
55 this.document = documentBuilder.newDocument();
56 this.ruleSetFileNames = new HashSet<String>();
57
58 Element ruleSetElement = createRuleSetElement(ruleSet);
59 document.appendChild(ruleSetElement);
60
61 TransformerFactory transformerFactory = TransformerFactory.newInstance();
62 Transformer transformer = transformerFactory.newTransformer();
63 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
64
65 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
66 try {
67 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
68 } catch (IllegalArgumentException e) {
69
70 }
71 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
72 transformer.transform(new DOMSource(document), new StreamResult(outputStream));
73 } catch (DOMException e) {
74 throw new RuntimeException(e);
75 } catch (FactoryConfigurationError e) {
76 throw new RuntimeException(e);
77 } catch (ParserConfigurationException e) {
78 throw new RuntimeException(e);
79 } catch (TransformerException e) {
80 throw new RuntimeException(e);
81 }
82 }
83
84 private Element createRuleSetElement(RuleSet ruleSet) {
85 Element ruleSetElement = document.createElement("ruleset");
86 if (outputNamespace) {
87
88 ruleSetElement.setAttribute("xmlns", "http://pmd.sf.net/ruleset/1.0.0");
89 ruleSetElement.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation",
90 "http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd");
91 ruleSetElement.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xsi:noNamespaceSchemaLocation",
92 "http://pmd.sf.net/ruleset_xml_schema.xsd");
93 }
94 ruleSetElement.setAttribute("name", ruleSet.getName());
95
96 if (ruleSet.getLanguage() != null) {
97 ruleSetElement.setAttribute("language", ruleSet.getLanguage().getName());
98 }
99
100 Element descriptionElement = createDescriptionElement(ruleSet.getDescription());
101 ruleSetElement.appendChild(descriptionElement);
102
103 for (String excludePattern : ruleSet.getExcludePatterns()) {
104 Element excludePatternElement = createExcludePatternElement(excludePattern);
105 ruleSetElement.appendChild(excludePatternElement);
106 }
107 for (String includePattern : ruleSet.getIncludePatterns()) {
108 Element includePatternElement = createIncludePatternElement(includePattern);
109 ruleSetElement.appendChild(includePatternElement);
110 }
111 for (Rule rule : ruleSet.getRules()) {
112 Element ruleElement = createRuleElement(rule);
113 if (ruleElement != null) {
114 ruleSetElement.appendChild(ruleElement);
115 }
116 }
117
118 return ruleSetElement;
119 }
120
121 private Element createDescriptionElement(String description) {
122 return createTextElement("description", description);
123 }
124
125 private Element createExcludePatternElement(String excludePattern) {
126 return createTextElement("exclude-pattern", excludePattern);
127 }
128
129 private Element createIncludePatternElement(String includePattern) {
130 return createTextElement("include-pattern", includePattern);
131 }
132
133 private Element createRuleElement(Rule rule) {
134 if (rule instanceof RuleReference) {
135 RuleReference ruleReference = (RuleReference)rule;
136 RuleSetReference ruleSetReference = ruleReference.getRuleSetReference();
137 if (ruleSetReference.isAllRules()) {
138 if (!ruleSetFileNames.contains(ruleSetReference.getRuleSetFileName())) {
139 ruleSetFileNames.add(ruleSetReference.getRuleSetFileName());
140 Element ruleSetReferenceElement = createRuleSetReferenceElement(ruleSetReference);
141 return ruleSetReferenceElement;
142 } else {
143 return null;
144 }
145 } else {
146 String name = ruleReference.getOverriddenName();
147 String ref = ruleReference.getRuleSetReference().getRuleSetFileName() + "/" + ruleReference.getName();
148 String message = ruleReference.getOverriddenMessage();
149 String externalInfoUrl = ruleReference.getOverriddenExternalInfoUrl();
150 String description = ruleReference.getOverriddenDescription();
151 Integer priority = ruleReference.getOverriddenPriority();
152 Properties properties = ruleReference.getOverriddenProperties();
153 List<String> examples = ruleReference.getOverriddenExamples();
154 return createSingleRuleElement(name, null, ref, message, externalInfoUrl, null, null, null,
155 description, priority, properties, examples);
156 }
157 } else {
158 return createSingleRuleElement(rule.getName(), rule.getSince(), null, rule.getMessage(),
159 rule.getExternalInfoUrl(), rule.getRuleClass(), rule.usesDFA(), rule.usesTypeResolution(),
160 rule.getDescription(), rule.getPriority(), rule.getProperties(), rule.getExamples());
161 }
162 }
163
164 private Element createSingleRuleElement(String name, String since, String ref, String message,
165 String externalInfoUrl, String clazz, Boolean dfa, Boolean typeResolution, String description,
166 Integer priority, Properties properties, List<String> examples) {
167 Element ruleElement = document.createElement("rule");
168 if (name != null) {
169 ruleElement.setAttribute("name", name);
170 }
171 if (since != null) {
172 ruleElement.setAttribute("since", since);
173 }
174 if (ref != null) {
175 ruleElement.setAttribute("ref", ref);
176 }
177 if (message != null) {
178 ruleElement.setAttribute("message", message);
179 }
180 if (externalInfoUrl != null) {
181 ruleElement.setAttribute("externalInfoUrl", externalInfoUrl);
182 }
183 if (clazz != null) {
184 ruleElement.setAttribute("class", clazz);
185 }
186 if (dfa != null) {
187 ruleElement.setAttribute("dfa", dfa.toString());
188 }
189 if (typeResolution != null) {
190 ruleElement.setAttribute("typeResolution", typeResolution.toString());
191 }
192
193 if (description != null) {
194 Element descriptionElement = createDescriptionElement(description);
195 ruleElement.appendChild(descriptionElement);
196 }
197 if (priority != null) {
198 Element priorityElement = createPriorityElement(priority);
199 ruleElement.appendChild(priorityElement);
200 }
201 if (properties != null) {
202 Element propertiesElement = createPropertiesElement(properties);
203 if (propertiesElement != null) {
204 ruleElement.appendChild(propertiesElement);
205 }
206 }
207 if (examples != null) {
208 for (String example : examples) {
209 Element exampleElement = createExampleElement(example);
210 ruleElement.appendChild(exampleElement);
211 }
212 }
213 return ruleElement;
214 }
215
216 private Element createRuleSetReferenceElement(RuleSetReference ruleSetReference) {
217 Element ruleSetReferenceElement = document.createElement("rule");
218 ruleSetReferenceElement.setAttribute("ref", ruleSetReference.getRuleSetFileName());
219 for (String exclude : ruleSetReference.getExcludes()) {
220 Element excludeElement = createExcludeElement(exclude);
221 ruleSetReferenceElement.appendChild(excludeElement);
222 }
223 return ruleSetReferenceElement;
224 }
225
226 private Element createExcludeElement(String exclude) {
227 return createTextElement("exclude", exclude);
228 }
229
230 private Element createExampleElement(String example) {
231 return createCDATASectionElement("example", example);
232 }
233
234 private Element createPriorityElement(Integer priority) {
235 return createTextElement("priority", priority.toString());
236 }
237
238 private Element createPropertiesElement(Properties properties) {
239 if (properties != null && !properties.isEmpty()) {
240 Element propertiesElement = document.createElement("properties");
241 for (Map.Entry<Object, Object> entry : properties.entrySet()) {
242 Element propertyElement = createPropertyElement(properties, (String)entry.getKey(),
243 (String)entry.getValue());
244 if (propertyElement != null) {
245 propertiesElement.appendChild(propertyElement);
246 }
247 }
248 return propertiesElement;
249 } else {
250 return null;
251 }
252 }
253
254 private Element createPropertyElement(Properties properties, String key, String value) {
255 Element propertyElement = document.createElement("property");
256 propertyElement.setAttribute("name", key);
257 if ("xpath".equals(key)) {
258 if (properties.containsKey("pluginname")) {
259 propertyElement.setAttribute("pluginname", properties.getProperty("pluginname"));
260 }
261 Element valueElement = createCDATASectionElement("value", value);
262 propertyElement.appendChild(valueElement);
263 } else if ("pluginname".equals(key)) {
264 if (properties.containsKey("xpath")) {
265 return null;
266 } else {
267 propertyElement.setAttribute("value", value);
268 }
269 } else {
270 propertyElement.setAttribute("value", value);
271 }
272
273 return propertyElement;
274 }
275
276 private Element createTextElement(String name, String value) {
277 Element element = document.createElement(name);
278 Text text = document.createTextNode(value);
279 element.appendChild(text);
280 return element;
281 }
282
283 private Element createCDATASectionElement(String name, String value) {
284 Element element = document.createElement(name);
285 CDATASection cdataSection = document.createCDATASection(value);
286 element.appendChild(cdataSection);
287 return element;
288 }
289 }