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 }