View Javadoc

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  			// This is as close to pretty printing as we'll get using standard Java APIs.
65  			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
66  			try {
67  			    transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3");
68  			} catch (IllegalArgumentException e) {
69  			    // Not Apache
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  			// TODO Should we keep track of schema versions?
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 }