1 /***
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd;
5
6 import net.sourceforge.pmd.util.ResourceLoader;
7 import org.w3c.dom.Document;
8 import org.w3c.dom.Element;
9 import org.w3c.dom.Node;
10 import org.w3c.dom.NodeList;
11
12 import javax.xml.parsers.DocumentBuilder;
13 import javax.xml.parsers.DocumentBuilderFactory;
14 import java.io.IOException;
15 import java.io.InputStream;
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Properties;
21 import java.util.Set;
22 import java.util.StringTokenizer;
23
24 public class RuleSetFactory {
25 private ClassLoader classLoader;
26
27 /***
28 * Returns an Iterator of RuleSet objects loaded from descriptions from
29 * the "rulesets.properties" resource or from the "rulesets.filenames" property.
30 * @return an iterator on RuleSet objects
31 */
32 public Iterator getRegisteredRuleSets() throws RuleSetNotFoundException {
33 try {
34 Properties props = new Properties();
35 props.load(ResourceLoader.loadResourceAsStream("rulesets/rulesets.properties"));
36 String rulesetFilenames = props.getProperty("rulesets.filenames");
37 List ruleSets = new ArrayList();
38 for (StringTokenizer st = new StringTokenizer(rulesetFilenames, ","); st.hasMoreTokens();) {
39 ruleSets.add(createRuleSet(st.nextToken()));
40 }
41 return ruleSets.iterator();
42 } catch (IOException ioe) {
43 throw new RuntimeException("Couldn't find rulesets.properties; please ensure that the rulesets directory is on the classpath. Here's the current classpath: " + System.getProperty("java.class.path"));
44 }
45 }
46
47 /***
48 * Create a ruleset from a name or from a list of name
49 * @param name name of rule set file loaded as a resource
50 * @param classLoader the classloader used to load the ruleset and subsequent rules
51 * @return the new ruleset
52 * @throws RuleSetNotFoundException
53 */
54 public RuleSet createRuleSet(String name, ClassLoader classLoader) throws RuleSetNotFoundException {
55 RuleSet ruleSet = null;
56 setClassLoader(classLoader);
57
58 if (name.indexOf(',') == -1) {
59 ruleSet = createRuleSet(tryToGetStreamTo(name, classLoader));
60 } else {
61 ruleSet = new RuleSet();
62 for (StringTokenizer st = new StringTokenizer(name, ","); st.hasMoreTokens();) {
63 String ruleSetName = st.nextToken().trim();
64 RuleSet tmpRuleSet = createRuleSet(ruleSetName, classLoader);
65 ruleSet.addRuleSet(tmpRuleSet);
66 }
67 }
68
69 return ruleSet;
70 }
71
72 /***
73 * Creates a ruleset. If passed a comma-delimited string (rulesets/basic.xml,rulesets/unusedcode.xml)
74 * it will parse that string and create a new ruleset for each item in the list.
75 * Same as createRuleSet(name, ruleSetFactory.getClassLoader()).
76 */
77 public RuleSet createRuleSet(String name) throws RuleSetNotFoundException {
78 return createRuleSet(name, getClass().getClassLoader());
79 }
80
81 /***
82 * Create a ruleset from an inputsteam.
83 * Same as createRuleSet(inputStream, ruleSetFactory.getClassLoader()).
84 * @param inputStream an input stream that contains a ruleset descripion
85 * @return a new ruleset
86 */
87 public RuleSet createRuleSet(InputStream inputStream) {
88 return createRuleSet(inputStream, getClass().getClassLoader());
89 }
90
91 /***
92 * Create a ruleset from an input stream with a specified class loader
93 * @param inputStream an input stream that contains a ruleset descripion
94 * @param classLoader a class loader used to load rule classes
95 * @return a new ruleset
96 */
97 public RuleSet createRuleSet(InputStream inputStream, ClassLoader classLoader) {
98 try {
99 setClassLoader(classLoader);
100 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
101 Document doc = builder.parse(inputStream);
102 Element root = doc.getDocumentElement();
103
104 RuleSet ruleSet = new RuleSet();
105 ruleSet.setName(root.getAttribute("name"));
106
107 NodeList nodeList = root.getChildNodes();
108 for (int i = 0; i < nodeList.getLength(); i++) {
109 Node node = nodeList.item(i);
110 if (node.getNodeType() == Node.ELEMENT_NODE) {
111 if (node.getNodeName().equals("description")) {
112 parseDescriptionNode(ruleSet, node);
113 } else if (node.getNodeName().equals("rule")) {
114 parseRuleNode(ruleSet, node);
115 }
116 }
117 }
118
119 return ruleSet;
120 } catch (Exception e) {
121 e.printStackTrace();
122 throw new RuntimeException("Couldn't read from that source: " + e.getMessage());
123 }
124 }
125
126 /***
127 * Return the class loader used to load ruleset resources and rules
128 * @return
129 */
130 public ClassLoader getClassLoader() {
131 return classLoader;
132 }
133
134 /***
135 * Sets the class loader used to load ruleset resources and rules
136 * @param loader a class loader
137 */
138 public void setClassLoader(ClassLoader loader) {
139 classLoader = loader;
140 }
141
142 /***
143 * Try to load a resource with the specified class loader
144 * @param name a resource name (contains a ruleset description)
145 * @param loader a class loader used to load that rule set description
146 * @return an inputstream to that resource
147 * @throws RuleSetNotFoundException
148 */
149 private InputStream tryToGetStreamTo(String name, ClassLoader loader) throws RuleSetNotFoundException {
150 InputStream in = ResourceLoader.loadResourceAsStream(name, loader);
151 if (in == null) {
152 throw new RuleSetNotFoundException("Can't find resource " + name + ". Make sure the resource is a valid file or URL or is on the CLASSPATH");
153 }
154
155 return in;
156 }
157
158 /***
159 * Parse a ruleset description node
160 * @param ruleSet the ruleset being constructed
161 * @param descriptionNode must be a description element node
162 */
163 private void parseDescriptionNode(RuleSet ruleSet, Node descriptionNode) {
164 NodeList nodeList = descriptionNode.getChildNodes();
165 StringBuffer buffer = new StringBuffer();
166 for (int i = 0 ; i < nodeList.getLength(); i++) {
167 Node node = nodeList.item(i);
168 if (node.getNodeType() == Node.TEXT_NODE) {
169 buffer.append(node.getNodeValue());
170 } else if (node.getNodeType() == Node.CDATA_SECTION_NODE) {
171 buffer.append(node.getNodeValue());
172 }
173 }
174 ruleSet.setDescription(buffer.toString());
175 }
176
177 /***
178 * Parse a rule node
179 * @param ruleSet the ruleset being constructed
180 * @param ruleElement must be a rule element node
181 */
182 private void parseRuleNode(RuleSet ruleSet, Node ruleNode) throws ClassNotFoundException, InstantiationException, IllegalAccessException, RuleSetNotFoundException {
183 Element ruleElement = (Element) ruleNode;
184 String ref = ruleElement.getAttribute("ref");
185 if (ref.trim().length() == 0) {
186 parseInternallyDefinedRuleNode(ruleSet, ruleNode);
187 } else {
188 parseExternallyDefinedRuleNode(ruleSet, ruleNode);
189 }
190 }
191
192 /***
193 * Process a rule definition node
194 * @param ruleSet the ruleset being constructed
195 * @param ruleNode must be a rule element node
196 */
197 private void parseInternallyDefinedRuleNode(RuleSet ruleSet, Node ruleNode) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
198 Element ruleElement = (Element) ruleNode;
199
200 String className = ruleElement.getAttribute("class");
201 String name = ruleElement.getAttribute("name");
202 String message = ruleElement.getAttribute("message");
203 Rule rule = (Rule) getClassLoader().loadClass(className).newInstance();
204 rule.setName(name);
205 rule.setMessage(message);
206
207 NodeList nodeList = ruleElement.getChildNodes();
208 for (int i = 0; i < nodeList.getLength(); i++) {
209 Node node = nodeList.item(i);
210 if (node.getNodeType() == Node.ELEMENT_NODE) {
211 if (node.getNodeName().equals("description")) {
212 parseDescriptionNode(rule, node);
213 } else if (node.getNodeName().equals("example")) {
214 parseExampleNode(rule, node);
215 } else if (node.getNodeName().equals("priority")) {
216 parsePriorityNode(rule, node);
217 } else if (node.getNodeName().equals("properties")) {
218 parsePropertiesNode(rule, node);
219 }
220 }
221 }
222
223 ruleSet.addRule(rule);
224 }
225
226 /***
227 * Process a reference to a rule
228 * @param ruleSet the ruleset being constructucted
229 * @param ruleNode must be a ruke element node
230 */
231 private void parseExternallyDefinedRuleNode(RuleSet ruleSet, Node ruleNode) throws RuleSetNotFoundException {
232 Element ruleElement = (Element) ruleNode;
233 String ref = ruleElement.getAttribute("ref");
234 if (ref.endsWith("xml")) {
235 parseRuleNodeWithExclude(ruleSet, ruleElement, ref);
236 } else {
237 parseRuleNodeWithSimpleReference(ruleSet, ref);
238 }
239 }
240
241 /***
242 * Parse a rule node with a simple reference
243 * @param ruleSet the ruleset being constructed
244 * @param ref a reference to a rule
245 */
246 private void parseRuleNodeWithSimpleReference(RuleSet ruleSet, String ref) throws RuleSetNotFoundException {
247 RuleSetFactory rsf = new RuleSetFactory();
248 ExternalRuleID externalRuleID = new ExternalRuleID(ref);
249 RuleSet externalRuleSet = rsf.createRuleSet(ResourceLoader.loadResourceAsStream(externalRuleID.getFilename()));
250 ruleSet.addRule(externalRuleSet.getRuleByName(externalRuleID.getRuleName()));
251 }
252
253 /***
254 * Parse a reference rule node with excludes
255 * @param ruleSet the ruleset being constructed
256 * @param ruleElement must be a rule element
257 * @param ref the ruleset reference
258 */
259 private void parseRuleNodeWithExclude(RuleSet ruleSet, Element ruleElement, String ref) throws RuleSetNotFoundException {
260 NodeList excludeNodes = ruleElement.getChildNodes();
261 Set excludes = new HashSet();
262 for (int i=0; i<excludeNodes.getLength(); i++) {
263 Node node = excludeNodes.item(i);
264 if ((node.getNodeType() == Node.ELEMENT_NODE) && (node.getNodeName().equals("exclude"))) {
265 Element excludeElement = (Element) node;
266 excludes.add(excludeElement.getAttribute("name"));
267 }
268 }
269
270 RuleSetFactory rsf = new RuleSetFactory();
271 RuleSet externalRuleSet = rsf.createRuleSet(ResourceLoader.loadResourceAsStream(ref));
272 for (Iterator i = externalRuleSet.getRules().iterator(); i.hasNext();) {
273 Rule rule = (Rule) i.next();
274 if (!excludes.contains(rule.getName())) {
275 ruleSet.addRule(rule);
276 }
277 }
278 }
279
280 /***
281 * Process a rule descrtiprion node
282 * @param rule the rule being constructed
283 * @param descriptionNode must be a description element node
284 */
285 private void parseDescriptionNode(Rule rule, Node descriptionNode) {
286 NodeList nodeList = descriptionNode.getChildNodes();
287 StringBuffer buffer = new StringBuffer();
288 for (int i = 0; i < nodeList.getLength(); i++) {
289 Node node = nodeList.item(i);
290 if (node.getNodeType() == Node.CDATA_SECTION_NODE) {
291 buffer.append(node.getNodeValue());
292 } else if (node.getNodeType() == Node.TEXT_NODE) {
293 buffer.append(node.getNodeValue());
294 }
295 }
296 rule.setDescription(buffer.toString());
297 }
298
299 /***
300 * Process a rule example node
301 * @param rule the rule being constructed
302 * @param exampleNode must be a example element node
303 */
304 private void parseExampleNode(Rule rule, Node exampleNode) {
305 NodeList nodeList = exampleNode.getChildNodes();
306 StringBuffer buffer = new StringBuffer();
307 for (int i = 0; i < nodeList.getLength(); i++) {
308 Node node = nodeList.item(i);
309 if (node.getNodeType() == Node.CDATA_SECTION_NODE) {
310 buffer.append(node.getNodeValue());
311 } else if (node.getNodeType() == Node.TEXT_NODE) {
312 buffer.append(node.getNodeValue());
313 }
314 }
315 rule.setExample(buffer.toString());
316 }
317
318 /***
319 * Parse a priority node
320 * @param rule the rule being constructed
321 * @param priorityNode must be a priority element
322 */
323 private void parsePriorityNode(Rule rule, Node priorityNode) {
324 StringBuffer buffer = new StringBuffer();
325 NodeList nodeList = priorityNode.getChildNodes();
326 for (int i = 0; i < nodeList.getLength(); i++) {
327 Node node = nodeList.item(i);
328 if (node.getNodeType() == Node.TEXT_NODE) {
329 buffer.append(node.getNodeValue());
330 }
331 }
332 rule.setPriority(new Integer(buffer.toString().trim()).intValue());
333 }
334
335 /***
336 * Parse a properties node
337 * @param rule the rule being constructed
338 * @param propertiesNode must be a properties element node
339 */
340 private void parsePropertiesNode(Rule rule, Node propertiesNode) {
341 NodeList nodeList = propertiesNode.getChildNodes();
342 for (int i = 0; i < nodeList.getLength(); i++) {
343 Node node = nodeList.item(i);
344 if ((node.getNodeType() == Node.ELEMENT_NODE) && (node.getNodeName().equals("property"))) {
345 parsePropertyNode(rule, node);
346 }
347 }
348 }
349
350 /***
351 * Parse a property node
352 * @param rule the rule being constructed
353 * @param propertyNode must be a property element node
354 */
355 private void parsePropertyNode(Rule rule, Node propertyNode) {
356 Element propertyElement = (Element) propertyNode;
357 String name = propertyElement.getAttribute("name");
358 String value = propertyElement.getAttribute("value");
359 if (value.trim().length() == 0) {
360 NodeList nodeList = propertyNode.getChildNodes();
361 for (int i = 0; i < nodeList.getLength(); i++) {
362 Node node = nodeList.item(i);
363 if ((node.getNodeType() == Node.ELEMENT_NODE) && (node.getNodeName().equals("value"))) {
364 value = parseValueNode(node);
365 }
366 }
367 }
368 rule.addProperty(name, value);
369 }
370
371 /***
372 * Parse a value node
373 * @param valueNode must be a value element node
374 * @return the value
375 */
376 private String parseValueNode(Node valueNode) {
377 StringBuffer buffer = new StringBuffer();
378 NodeList nodeList = valueNode.getChildNodes();
379 for (int i = 0; i < nodeList.getLength(); i++) {
380 Node node = nodeList.item(i);
381 if (node.getNodeType() == Node.CDATA_SECTION_NODE) {
382 buffer.append(node.getNodeValue());
383 } else if (node.getNodeType() == Node.TEXT_NODE) {
384 buffer.append(node.getNodeValue());
385 }
386 }
387 return buffer.toString();
388 }
389 }
This page was automatically generated by Maven