Clover coverage report - PMD - 3.9 Coverage timestamp: Tue Dec 19 2006 09:38:44 EST FRAMES NO FRAMES |
file stats: | LOC: | 469 |  | Methods: | 20
| NCLOC: | 314 |  | Classes: | 2
Source file | Conditionals | Statements | Methods | TOTAL
RuleSetFactory.java | 89.1% | 82.2% | 90% | 85.1% |

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 |  | import org.xml.sax.SAXException;
12 |  |
13 |  | import javax.xml.parsers.DocumentBuilder;
14 |  | import javax.xml.parsers.DocumentBuilderFactory;
15 |  | import javax.xml.parsers.ParserConfigurationException;
16 |  | import java.io.IOException;
17 |  | import java.io.InputStream;
18 |  | import java.util.HashSet;
19 |  | import java.util.Iterator;
20 |  | import java.util.Properties;
21 |  | import java.util.Set;
22 |  | import java.util.StringTokenizer;
23 |  |
24 |  | import net.sourceforge.pmd.rules.XPathRule;
25 |  |
26 |  | // Note that ruleset parsing may fail on JDK 1.6 beta
27 |  | // due to this bug - http://www.netbeans.org/issues/show_bug.cgi?id=63257
28 |  |
29 |  | public class RuleSetFactory {
30 |  |
31 |  | private static class OverrideParser {
32 |  | private Element ruleElement;
33 |  |
34 | 14 | public OverrideParser(Element ruleElement) {
35 | 14 | this.ruleElement = ruleElement;
36 |  | }
37 |  |
38 | 14 | public void overrideAsNecessary(Rule rule) {
39 | 14 | if (ruleElement.hasAttribute("name")) {
40 | 1 | rule.setName(ruleElement.getAttribute("name"));
41 |  | }
42 | 14 | if (ruleElement.hasAttribute("message")) {
43 | 4 | rule.setMessage(ruleElement.getAttribute("message"));
44 |  | }
45 | 14 | if (ruleElement.hasAttribute("externalInfoUrl")) {
46 | 0 | rule.setExternalInfoUrl(ruleElement.getAttribute("externalInfoUrl"));
47 |  | }
48 | 14 | for (int i = 0; i < ruleElement.getChildNodes().getLength(); i++) {
49 | 16 | Node node = ruleElement.getChildNodes().item(i);
50 | 16 | if (node.getNodeType() == Node.ELEMENT_NODE) {
51 | 6 | if (node.getNodeName().equals("description")) {
52 | 1 | rule.setDescription(parseTextNode(node));
53 | 5 | } else if (node.getNodeName().equals("example")) {
54 | 1 | rule.setExample(parseTextNode(node));
55 | 4 | } else if (node.getNodeName().equals("priority")) {
56 | 2 | rule.setPriority(Integer.parseInt(parseTextNode(node)));
57 | 2 | } else if (node.getNodeName().equals("properties")) {
58 | 2 | Properties p = new Properties();
59 | 2 | parsePropertiesNode(p, node);
60 | 2 | rule.addProperties(p);
61 |  | }
62 |  | }
63 |  | }
64 |  | }
65 |  | }
66 |  |
67 |  | private int minPriority = Rule.LOWEST_PRIORITY;
68 |  |
69 | 2 | public void setMinimumPriority(int minPriority) {
70 | 2 | this.minPriority = minPriority;
71 |  | }
72 |  |
73 |  | /**
74 |  | * Returns an Iterator of RuleSet objects loaded from descriptions from the
75 |  | * "rulesets.properties" resource.
76 |  | *
77 |  | * @return an iterator of RuleSet objects
78 |  | */
79 | 0 | public Iterator getRegisteredRuleSets() throws RuleSetNotFoundException {
80 | 0 | try {
81 | 0 | Properties props = new Properties();
82 | 0 | props.load(ResourceLoader.loadResourceAsStream("rulesets/rulesets.properties"));
83 | 0 | String rulesetFilenames = props.getProperty("rulesets.filenames");
84 | 0 | return createRuleSets(rulesetFilenames).getRuleSetsIterator();
85 |  | } catch (IOException ioe) {
86 | 0 | throw new RuntimeException("Couldn't find rulesets.properties; please ensure that the rulesets directory is on the classpath. Here's the current classpath: "
87 |  | + System.getProperty("java.class.path"));
88 |  | }
89 |  | }
90 |  |
91 |  | /**
92 |  | * Create a RuleSets from a list of names.
93 |  | *
94 |  | * @param ruleSetFileNames comma-separated list of rule set files.
95 |  | * @param classLoader the classloader to load the rulesets
96 |  | * @throws RuleSetNotFoundException
97 |  | */
98 | 218 | public RuleSets createRuleSets(String ruleSetFileNames, ClassLoader classLoader)
99 |  | throws RuleSetNotFoundException {
100 | 218 | RuleSets ruleSets = new RuleSets();
101 |  |
102 | 218 | for (StringTokenizer st = new StringTokenizer(ruleSetFileNames, ","); st
103 |  | .hasMoreTokens();) {
104 | 218 | RuleSet ruleSet = createSingleRuleSet(st.nextToken().trim(), classLoader);
105 | 218 | ruleSets.addRuleSet(ruleSet);
106 |  | }
107 |  |
108 | 218 | return ruleSets;
109 |  | }
110 |  |
111 |  | /**
112 |  | * Create a RuleSets from a list of names, using the classloader of this class.
113 |  | *
114 |  | * @param ruleSetFileNames comma-separated list of rule set files.
115 |  | * @throws RuleSetNotFoundException
116 |  | */
117 | 218 | public RuleSets createRuleSets(String ruleSetFileNames)
118 |  | throws RuleSetNotFoundException {
119 | 218 | return createRuleSets(ruleSetFileNames, getClass().getClassLoader());
120 |  | }
121 |  |
122 |  | /**
123 |  | * Create a ruleset from a name or from a list of names
124 |  | *
125 |  | * @param name name of rule set file loaded as a resource
126 |  | * @param classLoader the classloader used to load the ruleset and subsequent rules
127 |  | * @return the new ruleset
128 |  | * @throws RuleSetNotFoundException
129 |  | * @deprecated Use createRuleSets instead, because this method puts all rules in one
130 |  | * single RuleSet object, and thus removes name and language of the
131 |  | * originating rule set files.
132 |  | */
133 | 0 | public RuleSet createRuleSet(String name, ClassLoader classLoader) throws RuleSetNotFoundException {
134 | 0 | RuleSets ruleSets = createRuleSets(name, classLoader);
135 | 0 | RuleSet result = new RuleSet();
136 | 0 | RuleSet[] allRuleSets = ruleSets.getAllRuleSets();
137 | 0 | for (int i = 0; i < allRuleSets.length; i++) {
138 | 0 | result.addRuleSet(allRuleSets[i]);
139 |  | }
140 | 0 | return result;
141 |  | }
142 |  |
143 |  | /**
144 |  | * Create a ruleset from a name
145 |  | *
146 |  | * @param ruleSetFileName name of rule set file loaded as a resource
147 |  | * @param classLoader the classloader used to load the ruleset and subsequent rules
148 |  | * @return the new ruleset
149 |  | * @throws RuleSetNotFoundException
150 |  | */
151 | 218 | private RuleSet createSingleRuleSet(String ruleSetFileName, ClassLoader classLoader)
152 |  | throws RuleSetNotFoundException {
153 | 218 | return createRuleSet(tryToGetStreamTo(ruleSetFileName, classLoader), classLoader);
154 |  | }
155 |  |
156 |  | /**
157 |  | * Create a ruleset from a name
158 |  | *
159 |  | * @param ruleSetFileName name of rule set file loaded as a resource
160 |  | * @return the new ruleset
161 |  | * @throws RuleSetNotFoundException
162 |  | */
163 | 2 | public RuleSet createSingleRuleSet(String ruleSetFileName)
164 |  | throws RuleSetNotFoundException {
165 | 2 | return createRuleSet(tryToGetStreamTo(ruleSetFileName, getClass()
166 |  | .getClassLoader()));
167 |  | }
168 |  |
169 |  | /**
170 |  | * Create a ruleset from an inputsteam. Same as createRuleSet(inputStream,
171 |  | * ruleSetFactory.getClassLoader()).
172 |  | *
173 |  | * @param inputStream an input stream that contains a ruleset descripion
174 |  | * @return a new ruleset
175 |  | */
176 | 33 | public RuleSet createRuleSet(InputStream inputStream) {
177 | 33 | return createRuleSet(inputStream, getClass().getClassLoader());
178 |  | }
179 |  |
180 |  | /**
181 |  | * Create a ruleset from an input stream with a specified class loader
182 |  | *
183 |  | * @param inputStream an input stream that contains a ruleset descripion
184 |  | * @param classLoader a class loader used to load rule classes
185 |  | * @return a new ruleset
186 |  | */
187 | 251 | private RuleSet createRuleSet(InputStream inputStream, ClassLoader classLoader) {
188 | 251 | try {
189 | 251 | DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
190 | 251 | Document doc = builder.parse(inputStream);
191 | 251 | Element root = doc.getDocumentElement();
192 |  |
193 | 251 | RuleSet ruleSet = new RuleSet();
194 | 251 | ruleSet.setName(root.getAttribute("name"));
195 | 251 | ruleSet.setLanguage(Language.getByName(root.getAttribute("language")));
196 |  |
197 | 251 | NodeList nodeList = root.getChildNodes();
198 | 251 | for (int i = 0; i < nodeList.getLength(); i++) {
199 | 9866 | Node node = nodeList.item(i);
200 | 9866 | if (node.getNodeType() == Node.ELEMENT_NODE) {
201 | 4796 | if (node.getNodeName().equals("description")) {
202 | 250 | ruleSet.setDescription(parseTextNode(node));
203 | 4546 | } else if (node.getNodeName().equals("rule")) {
204 | 4543 | parseRuleNode(ruleSet, node, classLoader);
205 |  | }
206 |  | }
207 |  | }
208 |  |
209 | 250 | return ruleSet;
210 |  | } catch (ClassNotFoundException cnfe) {
211 | 0 | cnfe.printStackTrace();
212 | 0 | throw new RuntimeException("Couldn't find that class " + cnfe.getMessage());
213 |  | } catch (InstantiationException ie) {
214 | 0 | ie.printStackTrace();
215 | 0 | throw new RuntimeException("Couldn't find that class " + ie.getMessage());
216 |  | } catch (IllegalAccessException iae) {
217 | 0 | iae.printStackTrace();
218 | 0 | throw new RuntimeException("Couldn't find that class " + iae.getMessage());
219 |  | } catch (ParserConfigurationException pce) {
220 | 0 | pce.printStackTrace();
221 | 0 | throw new RuntimeException("Couldn't find that class " + pce.getMessage());
222 |  | } catch (RuleSetNotFoundException rsnfe) {
223 | 0 | rsnfe.printStackTrace();
224 | 0 | throw new RuntimeException("Couldn't find that class " + rsnfe.getMessage());
225 |  | } catch (IOException ioe) {
226 | 0 | ioe.printStackTrace();
227 | 0 | throw new RuntimeException("Couldn't find that class " + ioe.getMessage());
228 |  | } catch (SAXException se) {
229 | 0 | se.printStackTrace();
230 | 0 | throw new RuntimeException("Couldn't find that class " + se.getMessage());
231 |  | }
232 |  | }
233 |  |
234 |  | /**
235 |  | * Try to load a resource with the specified class loader
236 |  | *
237 |  | * @param name a resource name (contains a ruleset description)
238 |  | * @param loader a class loader used to load that rule set description
239 |  | * @return an inputstream to that resource
240 |  | * @throws RuleSetNotFoundException
241 |  | */
242 | 220 | private InputStream tryToGetStreamTo(String name, ClassLoader loader)
243 |  | throws RuleSetNotFoundException {
244 | 220 | InputStream in = ResourceLoader.loadResourceAsStream(name, loader);
245 | 220 | if (in == null) {
246 | 1 | throw new RuleSetNotFoundException("Can't find resource "
247 |  | + name
248 |  | + ". Make sure the resource is a valid file or URL or is on the CLASSPATH. Here's the current classpath: "
249 |  | + System.getProperty("java.class.path"));
250 |  | }
251 | 219 | return in;
252 |  | }
253 |  |
254 |  | /**
255 |  | * Parse a rule node
256 |  | *
257 |  | * @param ruleSet the ruleset being constructed
258 |  | * @param ruleNode must be a rule element node
259 |  | */
260 | 4543 | private void parseRuleNode(RuleSet ruleSet, Node ruleNode, ClassLoader classLoader)
261 |  | throws ClassNotFoundException, InstantiationException,
262 |  | IllegalAccessException, RuleSetNotFoundException {
263 | 4543 | Element ruleElement = (Element) ruleNode;
264 | 4543 | String ref = ruleElement.getAttribute("ref");
265 | 4543 | if (ref.trim().length() == 0) {
266 | 4526 | parseInternallyDefinedRuleNode(ruleSet, ruleNode, classLoader);
267 |  | } else {
268 | 17 | parseExternallyDefinedRuleNode(ruleSet, ruleNode);
269 |  | }
270 |  | }
271 |  |
272 |  | /**
273 |  | * Process a rule definition node
274 |  | *
275 |  | * @param ruleSet the ruleset being constructed
276 |  | * @param ruleNode must be a rule element node
277 |  | */
278 | 4526 | private void parseInternallyDefinedRuleNode(RuleSet ruleSet, Node ruleNode, ClassLoader classLoader)
279 |  | throws ClassNotFoundException, InstantiationException, IllegalAccessException {
280 | 4526 | Element ruleElement = (Element) ruleNode;
281 |  |
282 | 4526 | String attribute = ruleElement.getAttribute("class");
283 | 4526 | Class c;
284 | 4526 | if ((Language.JAVA.equals(ruleSet.getLanguage()) || ruleSet.getLanguage() == null) &&
285 |  | attribute.equals("net.sourceforge.pmd.rules.XPathRule")) {
286 | 2451 | String xpath = null;
287 | 2451 | for (int i = 0; i < ruleElement.getChildNodes().getLength(); i++) {
288 | 22095 | Node node = ruleElement.getChildNodes().item(i);
289 | 22095 | if (node.getNodeType() == Node.ELEMENT_NODE) {
290 | 9822 | if (node.getNodeName().equals("properties")) {
291 | 2451 | Properties p = new Properties();
292 | 2451 | parsePropertiesNode(p, node);
293 | 2451 | xpath = p.getProperty("xpath");
294 |  | }
295 |  | }
296 |  | }
297 | 2451 | c = XPathRule.loadClass(classLoader, xpath, ruleElement.getAttribute("name"));
298 |  | } else {
299 | 2075 | c = classLoader.loadClass(attribute);
300 |  | }
301 | 4526 | Rule rule = (Rule) c.newInstance();
302 |  |
303 | 4526 | rule.setName(ruleElement.getAttribute("name"));
304 | 4526 | rule.setMessage(ruleElement.getAttribute("message"));
305 | 4526 | rule.setRuleSetName(ruleSet.getName());
306 | 4526 | rule.setExternalInfoUrl(ruleElement.getAttribute("externalInfoUrl"));
307 |  |
308 | 4526 | if (ruleElement.hasAttribute("dfa")
309 |  | && ruleElement.getAttribute("dfa").equals("true")) {
310 | 18 | rule.setUsesDFA();
311 |  | }
312 |  |
313 | 4526 | if (ruleElement.hasAttribute("typeResolution")
314 |  | && ruleElement.getAttribute("typeResolution").equals("true")) {
315 | 4 | rule.setUsesTypeResolution();
316 |  | }
317 |  |
318 | 4526 | for (int i = 0; i < ruleElement.getChildNodes().getLength(); i++) {
319 | 37904 | Node node = ruleElement.getChildNodes().item(i);
320 | 37904 | if (node.getNodeType() == Node.ELEMENT_NODE) {
321 | 16698 | if (node.getNodeName().equals("description")) {
322 | 4480 | rule.setDescription(parseTextNode(node));
323 | 12218 | } else if (node.getNodeName().equals("example")) {
324 | 4568 | rule.setExample(parseTextNode(node));
325 | 7650 | } else if (node.getNodeName().equals("priority")) {
326 | 4468 | rule.setPriority(new Integer(parseTextNode(node).trim()).intValue());
327 | 3182 | } else if (node.getNodeName().equals("properties")) {
328 | 3182 | Properties p = new Properties();
329 | 3182 | parsePropertiesNode(p, node);
330 | 3182 | for (Iterator j = p.keySet().iterator(); j.hasNext();) {
331 | 3231 | String key = (String) j.next();
332 | 3231 | rule.addProperty(key, p.getProperty(key));
333 |  | }
334 |  | }
335 |  | }
336 |  | }
337 | 4526 | if (rule.getPriority() <= minPriority) {
338 | 4525 | ruleSet.addRule(rule);
339 |  | }
340 |  | }
341 |  |
342 |  | /**
343 |  | * Process a reference to a rule
344 |  | *
345 |  | * @param ruleSet the ruleset being constructucted
346 |  | * @param ruleNode must be a rule element node
347 |  | */
348 | 17 | private void parseExternallyDefinedRuleNode(RuleSet ruleSet, Node ruleNode)
349 |  | throws RuleSetNotFoundException {
350 | 17 | Element ruleElement = (Element) ruleNode;
351 | 17 | String ref = ruleElement.getAttribute("ref");
352 | 17 | if (ref.endsWith("xml")) {
353 | 2 | parseRuleNodeWithExclude(ruleSet, ruleElement, ref);
354 |  | } else {
355 | 15 | parseRuleNodeWithSimpleReference(ruleSet, ruleNode, ref);
356 |  | }
357 |  | }
358 |  |
359 |  | /**
360 |  | * Parse a rule node with a simple reference
361 |  | *
362 |  | * @param ruleSet the ruleset being constructed
363 |  | * @param ref a reference to a rule
364 |  | */
365 | 15 | private void parseRuleNodeWithSimpleReference(RuleSet ruleSet, Node ruleNode,
366 |  | String ref) throws RuleSetNotFoundException {
367 | 15 | RuleSetFactory rsf = new RuleSetFactory();
368 |  |
369 | 15 | ExternalRuleID externalRuleID = new ExternalRuleID(ref);
370 | 15 | RuleSet externalRuleSet = rsf.createRuleSet(ResourceLoader
371 |  | .loadResourceAsStream(externalRuleID.getFilename()));
372 | 15 | Rule externalRule = externalRuleSet.getRuleByName(externalRuleID.getRuleName());
373 | 15 | if (externalRule == null) {
374 | 1 | throw new IllegalArgumentException("Unable to find rule "
375 |  | + externalRuleID.getRuleName()
376 |  | + "; perhaps the rule name is mispelled?");
377 |  | }
378 |  |
379 | 14 | OverrideParser p = new OverrideParser((Element) ruleNode);
380 | 14 | p.overrideAsNecessary(externalRule);
381 |  |
382 | 14 | if (externalRule.getPriority() <= minPriority) {
383 | 14 | ruleSet.addRule(externalRule);
384 |  | }
385 |  | }
386 |  |
387 |  | /**
388 |  | * Parse a reference rule node with excludes
389 |  | *
390 |  | * @param ruleSet the ruleset being constructed
391 |  | * @param ruleElement must be a rule element
392 |  | * @param ref the ruleset reference
393 |  | */
394 | 2 | private void parseRuleNodeWithExclude(RuleSet ruleSet, Element ruleElement, String ref)
395 |  | throws RuleSetNotFoundException {
396 | 2 | NodeList excludeNodes = ruleElement.getChildNodes();
397 | 2 | Set excludes = new HashSet();
398 | 2 | for (int i = 0; i < excludeNodes.getLength(); i++) {
399 | 0 | if ((excludeNodes.item(i).getNodeType() == Node.ELEMENT_NODE)
400 |  | && (excludeNodes.item(i).getNodeName().equals("exclude"))) {
401 | 0 | Element excludeElement = (Element) excludeNodes.item(i);
402 | 0 | excludes.add(excludeElement.getAttribute("name"));
403 |  | }
404 |  | }
405 |  |
406 | 2 | RuleSetFactory rsf = new RuleSetFactory();
407 | 2 | RuleSet externalRuleSet = rsf.createRuleSet(ResourceLoader
408 |  | .loadResourceAsStream(ref));
409 | 2 | for (Iterator i = externalRuleSet.getRules().iterator(); i.hasNext();) {
410 | 34 | Rule rule = (Rule) i.next();
411 | 34 | if (!excludes.contains(rule.getName()) && rule.getPriority() <= minPriority) {
412 | 34 | ruleSet.addRule(rule);
413 |  | }
414 |  | }
415 |  | }
416 |  |
417 | 18995 | private static String parseTextNode(Node exampleNode) {
418 | 18995 | StringBuffer buffer = new StringBuffer();
419 | 18995 | for (int i = 0; i < exampleNode.getChildNodes().getLength(); i++) {
420 | 37327 | Node node = exampleNode.getChildNodes().item(i);
421 | 37327 | if (node.getNodeType() == Node.CDATA_SECTION_NODE
422 |  | || node.getNodeType() == Node.TEXT_NODE) {
423 | 37315 | buffer.append(node.getNodeValue());
424 |  | }
425 |  | }
426 | 18995 | return buffer.toString();
427 |  | }
428 |  |
429 |  | /**
430 |  | * Parse a properties node
431 |  | *
432 |  | * @param propertiesNode must be a properties element node
433 |  | */
434 | 5635 | private static void parsePropertiesNode(Properties p, Node propertiesNode) {
435 | 5635 | for (int i = 0; i < propertiesNode.getChildNodes().getLength(); i++) {
436 | 16843 | Node node = propertiesNode.getChildNodes().item(i);
437 | 16843 | if (node.getNodeType() == Node.ELEMENT_NODE
438 |  | && node.getNodeName().equals("property")) {
439 | 5695 | parsePropertyNode(p, node);
440 |  | }
441 |  | }
442 |  | }
443 |  |
444 |  | /**
445 |  | * Parse a property node
446 |  | *
447 |  | * @param propertyNode must be a property element node
448 |  | */
449 | 5695 | private static void parsePropertyNode(Properties p, Node propertyNode) {
450 | 5695 | Element propertyElement = (Element) propertyNode;
451 | 5695 | String name = propertyElement.getAttribute("name");
452 | 5695 | String value = propertyElement.getAttribute("value");
453 |  | // TODO String desc = propertyElement.getAttribute("description");
454 | 5695 | if (value.trim().length() == 0) {
455 | 5303 | for (int i = 0; i < propertyNode.getChildNodes().getLength(); i++) {
456 | 15869 | Node node = propertyNode.getChildNodes().item(i);
457 | 15869 | if ((node.getNodeType() == Node.ELEMENT_NODE)
458 |  | && node.getNodeName().equals("value")) {
459 | 5225 | value = parseTextNode(node);
460 |  | }
461 |  | }
462 |  | }
463 | 5695 | if (propertyElement.hasAttribute("pluginname")) {
464 | 191 | p.setProperty("pluginname", propertyElement.getAttributeNode("pluginname")
465 |  | .getNodeValue());
466 |  | }
467 | 5695 | p.setProperty(name, value);
468 |  | }
469 |  | }