Clover coverage report - PMD - 3.7
Coverage timestamp: Wed May 31 2006 09:25:59 EDT

file stats: | LOC: | 472 |  | Methods: | 21
| NCLOC: | 305 |  | Classes: | 2

Source file | Conditionals | Statements | Methods | TOTAL

RuleSetFactory.java

88.1%

83.1%

95.2%

85.7%

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