001: package net.sourceforge.pmd;
002:
003: import java.io.IOException;
004: import java.io.OutputStream;
005: import java.util.HashSet;
006: import java.util.List;
007: import java.util.Map;
008: import java.util.Properties;
009: import java.util.Set;
010:
011: import javax.xml.parsers.DocumentBuilder;
012: import javax.xml.parsers.DocumentBuilderFactory;
013: import javax.xml.parsers.FactoryConfigurationError;
014: import javax.xml.parsers.ParserConfigurationException;
015: import javax.xml.transform.OutputKeys;
016: import javax.xml.transform.Transformer;
017: import javax.xml.transform.TransformerException;
018: import javax.xml.transform.TransformerFactory;
019: import javax.xml.transform.dom.DOMSource;
020: import javax.xml.transform.stream.StreamResult;
021:
022: import org.w3c.dom.CDATASection;
023: import org.w3c.dom.DOMException;
024: import org.w3c.dom.Document;
025: import org.w3c.dom.Element;
026: import org.w3c.dom.Text;
027:
028: /**
029: * This class represents a way to serialize a RuleSet to an XML configuration file.
030: */
031: public class RuleSetWriter {
032: private final OutputStream outputStream;
033: private Document document;
034: private Set<String> ruleSetFileNames;
035:
036: public RuleSetWriter(OutputStream outputStream) {
037: this .outputStream = outputStream;
038: }
039:
040: public void close() throws IOException {
041: outputStream.flush();
042: outputStream.close();
043: }
044:
045: public void write(RuleSet ruleSet) {
046: try {
047: DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
048: .newInstance();
049: DocumentBuilder documentBuilder = documentBuilderFactory
050: .newDocumentBuilder();
051: this .document = documentBuilder.newDocument();
052: this .ruleSetFileNames = new HashSet<String>();
053:
054: Element ruleSetElement = createRuleSetElement(ruleSet);
055: document.appendChild(ruleSetElement);
056:
057: TransformerFactory transformerFactory = TransformerFactory
058: .newInstance();
059: Transformer transformer = transformerFactory
060: .newTransformer();
061: transformer.setOutputProperty(OutputKeys.METHOD, "xml");
062: // This is as close to pretty printing as we'll get using standard Java APIs.
063: transformer.setOutputProperty(OutputKeys.INDENT, "yes");
064: transformer.setOutputProperty(
065: "{http://xml.apache.org/xslt}indent-amount", "3");
066: transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
067: transformer.transform(new DOMSource(document),
068: new StreamResult(outputStream));
069: } catch (DOMException e) {
070: throw new RuntimeException(e);
071: } catch (FactoryConfigurationError e) {
072: throw new RuntimeException(e);
073: } catch (ParserConfigurationException e) {
074: throw new RuntimeException(e);
075: } catch (TransformerException e) {
076: throw new RuntimeException(e);
077: }
078: }
079:
080: private Element createRuleSetElement(RuleSet ruleSet) {
081: Element ruleSetElement = document.createElement("ruleset");
082: ruleSetElement.setAttribute("xmlns",
083: "http://pmd.sf.net/ruleset/1.0.0");
084: ruleSetElement
085: .setAttributeNS(
086: "http://www.w3.org/2001/XMLSchema-instance",
087: "xsi:schemaLocation",
088: "http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd");
089: ruleSetElement.setAttributeNS(
090: "http://www.w3.org/2001/XMLSchema-instance",
091: "xsi:noNamespaceSchemaLocation",
092: "http://pmd.sf.net/ruleset_xml_schema.xsd");
093: ruleSetElement.setAttribute("name", ruleSet.getName());
094:
095: if (ruleSet.getLanguage() != null) {
096: ruleSetElement.setAttribute("language", ruleSet
097: .getLanguage().getName());
098: }
099:
100: Element descriptionElement = createDescriptionElement(ruleSet
101: .getDescription());
102: ruleSetElement.appendChild(descriptionElement);
103:
104: for (String excludePattern : ruleSet.getExcludePatterns()) {
105: Element excludePatternElement = createExcludePatternElement(excludePattern);
106: ruleSetElement.appendChild(excludePatternElement);
107: }
108: for (String includePattern : ruleSet.getIncludePatterns()) {
109: Element includePatternElement = createIncludePatternElement(includePattern);
110: ruleSetElement.appendChild(includePatternElement);
111: }
112: for (Rule rule : ruleSet.getRules()) {
113: Element ruleElement = createRuleElement(rule);
114: if (ruleElement != null) {
115: ruleSetElement.appendChild(ruleElement);
116: }
117: }
118:
119: return ruleSetElement;
120: }
121:
122: private Element createDescriptionElement(String description) {
123: return createTextElement("description", description);
124: }
125:
126: private Element createExcludePatternElement(String excludePattern) {
127: return createTextElement("exclude-pattern", excludePattern);
128: }
129:
130: private Element createIncludePatternElement(String includePattern) {
131: return createTextElement("include-pattern", includePattern);
132: }
133:
134: private Element createRuleElement(Rule rule) {
135: if (rule instanceof RuleReference) {
136: RuleReference ruleReference = (RuleReference) rule;
137: RuleSetReference ruleSetReference = ruleReference
138: .getRuleSetReference();
139: if (ruleSetReference.isAllRules()) {
140: if (!ruleSetFileNames.contains(ruleSetReference
141: .getRuleSetFileName())) {
142: ruleSetFileNames.add(ruleSetReference
143: .getRuleSetFileName());
144: Element ruleSetReferenceElement = createRuleSetReferenceElement(ruleSetReference);
145: return ruleSetReferenceElement;
146: } else {
147: return null;
148: }
149: } else {
150: String name = ruleReference.getOverriddenName();
151: String ref = ruleReference.getRuleSetReference()
152: .getRuleSetFileName()
153: + "/" + ruleReference.getName();
154: String message = ruleReference.getOverriddenMessage();
155: String externalInfoUrl = ruleReference
156: .getOverriddenExternalInfoUrl();
157: String description = ruleReference
158: .getOverriddenDescription();
159: Integer priority = ruleReference
160: .getOverriddenPriority();
161: Properties properties = ruleReference
162: .getOverriddenProperties();
163: List<String> examples = ruleReference
164: .getOverriddenExamples();
165: return createSingleRuleElement(name, null, ref,
166: message, externalInfoUrl, null, null, null,
167: description, priority, properties, examples);
168: }
169: } else {
170: return createSingleRuleElement(rule.getName(), rule
171: .getSince(), null, rule.getMessage(), rule
172: .getExternalInfoUrl(), rule.getRuleClass(), rule
173: .usesDFA(), rule.usesTypeResolution(), rule
174: .getDescription(), rule.getPriority(), rule
175: .getProperties(), rule.getExamples());
176: }
177: }
178:
179: private Element createSingleRuleElement(String name, String since,
180: String ref, String message, String externalInfoUrl,
181: String clazz, Boolean dfa, Boolean typeResolution,
182: String description, Integer priority,
183: Properties properties, List<String> examples) {
184: Element ruleElement = document.createElement("rule");
185: if (name != null) {
186: ruleElement.setAttribute("name", name);
187: }
188: if (since != null) {
189: ruleElement.setAttribute("since", since);
190: }
191: if (ref != null) {
192: ruleElement.setAttribute("ref", ref);
193: }
194: if (message != null) {
195: ruleElement.setAttribute("message", message);
196: }
197: if (externalInfoUrl != null) {
198: ruleElement
199: .setAttribute("externalInfoUrl", externalInfoUrl);
200: }
201: if (clazz != null) {
202: ruleElement.setAttribute("class", clazz);
203: }
204: if (dfa != null) {
205: ruleElement.setAttribute("dfa", dfa.toString());
206: }
207: if (typeResolution != null) {
208: ruleElement.setAttribute("typeResolution", typeResolution
209: .toString());
210: }
211:
212: if (description != null) {
213: Element descriptionElement = createDescriptionElement(description);
214: ruleElement.appendChild(descriptionElement);
215: }
216: if (priority != null) {
217: Element priorityElement = createPriorityElement(priority);
218: ruleElement.appendChild(priorityElement);
219: }
220: if (properties != null) {
221: Element propertiesElement = createPropertiesElement(properties);
222: if (propertiesElement != null) {
223: ruleElement.appendChild(propertiesElement);
224: }
225: }
226: if (examples != null) {
227: for (String example : examples) {
228: Element exampleElement = createExampleElement(example);
229: ruleElement.appendChild(exampleElement);
230: }
231: }
232: return ruleElement;
233: }
234:
235: private Element createRuleSetReferenceElement(
236: RuleSetReference ruleSetReference) {
237: Element ruleSetReferenceElement = document
238: .createElement("rule");
239: ruleSetReferenceElement.setAttribute("ref", ruleSetReference
240: .getRuleSetFileName());
241: for (String exclude : ruleSetReference.getExcludes()) {
242: Element excludeElement = createExcludeElement(exclude);
243: ruleSetReferenceElement.appendChild(excludeElement);
244: }
245: return ruleSetReferenceElement;
246: }
247:
248: private Element createExcludeElement(String exclude) {
249: return createTextElement("exclude", exclude);
250: }
251:
252: private Element createExampleElement(String example) {
253: return createCDATASectionElement("example", example);
254: }
255:
256: private Element createPriorityElement(Integer priority) {
257: return createTextElement("priority", priority.toString());
258: }
259:
260: private Element createPropertiesElement(Properties properties) {
261: if (properties != null && !properties.isEmpty()) {
262: Element propertiesElement = document
263: .createElement("properties");
264: for (Map.Entry<Object, Object> entry : properties
265: .entrySet()) {
266: Element propertyElement = createPropertyElement(
267: properties, (String) entry.getKey(),
268: (String) entry.getValue());
269: if (propertyElement != null) {
270: propertiesElement.appendChild(propertyElement);
271: }
272: }
273: return propertiesElement;
274: } else {
275: return null;
276: }
277: }
278:
279: private Element createPropertyElement(Properties properties,
280: String key, String value) {
281: Element propertyElement = document.createElement("property");
282: propertyElement.setAttribute("name", key);
283: if ("xpath".equals(key)) {
284: if (properties.containsKey("pluginname")) {
285: propertyElement.setAttribute("pluginname", properties
286: .getProperty("pluginname"));
287: }
288: Element valueElement = createCDATASectionElement("value",
289: value);
290: propertyElement.appendChild(valueElement);
291: } else if ("pluginname".equals(key)) {
292: if (properties.containsKey("xpath")) {
293: return null;
294: } else {
295: propertyElement.setAttribute("value", value);
296: }
297: } else {
298: propertyElement.setAttribute("value", value);
299: }
300:
301: return propertyElement;
302: }
303:
304: private Element createTextElement(String name, String value) {
305: Element element = document.createElement(name);
306: Text text = document.createTextNode(value);
307: element.appendChild(text);
308: return element;
309: }
310:
311: private Element createCDATASectionElement(String name, String value) {
312: Element element = document.createElement(name);
313: CDATASection cdataSection = document.createCDATASection(value);
314: element.appendChild(cdataSection);
315: return element;
316: }
317: }
|