View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd;
5   
6   import static org.junit.Assert.assertEquals;
7   import static org.junit.Assert.assertFalse;
8   import static org.junit.Assert.assertNotNull;
9   import static org.junit.Assert.assertNotSame;
10  import static org.junit.Assert.assertNull;
11  import static org.junit.Assert.assertTrue;
12  import static org.junit.Assert.fail;
13  
14  import java.io.BufferedReader;
15  import java.io.ByteArrayInputStream;
16  import java.io.ByteArrayOutputStream;
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.io.InputStreamReader;
20  import java.util.ArrayList;
21  import java.util.HashSet;
22  import java.util.List;
23  import java.util.Properties;
24  import java.util.Set;
25  import java.util.StringTokenizer;
26  
27  import javax.xml.parsers.ParserConfigurationException;
28  import javax.xml.parsers.SAXParser;
29  import javax.xml.parsers.SAXParserFactory;
30  
31  import junit.framework.JUnit4TestAdapter;
32  import net.sourceforge.pmd.lang.Language;
33  import net.sourceforge.pmd.lang.LanguageVersion;
34  import net.sourceforge.pmd.lang.java.rule.unusedcode.UnusedLocalVariableRule;
35  import net.sourceforge.pmd.lang.rule.RuleReference;
36  import net.sourceforge.pmd.lang.rule.XPathRule;
37  import net.sourceforge.pmd.util.ResourceLoader;
38  
39  import org.junit.BeforeClass;
40  import org.junit.Test;
41  import org.xml.sax.InputSource;
42  import org.xml.sax.SAXException;
43  import org.xml.sax.SAXParseException;
44  import org.xml.sax.helpers.DefaultHandler;
45  
46  public class RuleSetFactoryTest {
47  	private static SAXParserFactory saxParserFactory;
48  	private static ValidateDefaultHandler validateDefaultHandlerXsd;
49  	private static ValidateDefaultHandler validateDefaultHandlerDtd;
50  	private static SAXParser saxParser;
51  	
52  	@BeforeClass
53  	public static void init() throws Exception {
54      	saxParserFactory = SAXParserFactory.newInstance();
55      	saxParserFactory.setValidating(true);
56      	saxParserFactory.setNamespaceAware(true);
57      	
58  		// Hope we're using Xerces, or this may not work!
59  		// Note: Features are listed here
60  		// http://xerces.apache.org/xerces2-j/features.html
61  		saxParserFactory.setFeature("http://xml.org/sax/features/validation",
62  				true);
63  		saxParserFactory.setFeature(
64  				"http://apache.org/xml/features/validation/schema", true);
65  		saxParserFactory
66  				.setFeature(
67  						"http://apache.org/xml/features/validation/schema-full-checking",
68  						true);
69      	
70      	validateDefaultHandlerXsd = new ValidateDefaultHandler("src/main/resources/ruleset_2_0_0.xsd");
71      	validateDefaultHandlerDtd = new ValidateDefaultHandler("src/main/resources/ruleset_2_0_0.dtd");
72      	
73      	saxParser = saxParserFactory.newSAXParser();
74  	}
75  	
76  	@Test
77  	public void testRuleSetFileName() throws RuleSetNotFoundException {
78  		RuleSet rs = loadRuleSet(EMPTY_RULESET);
79  		assertNull("RuleSet file name not expected", rs.getFileName());
80  
81  		RuleSetFactory rsf = new RuleSetFactory();
82  		rs = rsf.createRuleSet("rulesets/java/basic.xml");
83  		assertEquals("wrong RuleSet file name", rs.getFileName(),
84  				"rulesets/java/basic.xml");
85  	}
86  
87  	@Test
88  	public void testNoRuleSetFileName() throws RuleSetNotFoundException {
89  		RuleSet rs = loadRuleSet(EMPTY_RULESET);
90  		assertNull("RuleSet file name not expected", rs.getFileName());
91  	}
92  
93  	@Test
94  	public void testRefs() throws Throwable {
95  		InputStream in = ResourceLoader.loadResourceAsStream(
96  				"rulesets/java/migrating_to_15.xml", this.getClass()
97  						.getClassLoader());
98  		if (in == null) {
99  			throw new RuleSetNotFoundException(
100 					"Can't find resource   Make sure the resource is a valid file or URL or is on the CLASSPATH.  Here's the current classpath: "
101 							+ System.getProperty("java.class.path"));
102 		}
103 		RuleSetFactory rsf = new RuleSetFactory();
104 		RuleSet rs = rsf.createRuleSet("rulesets/java/migrating_to_15.xml");
105 		assertNotNull(rs.getRuleByName("AvoidEnumAsIdentifier"));
106 	}
107 
108 	@Test(expected = RuleSetNotFoundException.class)
109 	public void testRuleSetNotFound() throws RuleSetNotFoundException {
110 		RuleSetFactory rsf = new RuleSetFactory();
111 		rsf.createRuleSet("fooooo");
112 	}
113 
114 	@Test
115 	public void testCreateEmptyRuleSet() throws RuleSetNotFoundException {
116 		RuleSet rs = loadRuleSet(EMPTY_RULESET);
117 		assertEquals("test", rs.getName());
118 		assertEquals(0, rs.size());
119 	}
120 
121 	@Test
122 	public void testSingleRule() throws RuleSetNotFoundException {
123 		RuleSet rs = loadRuleSet(SINGLE_RULE);
124 		assertEquals(1, rs.size());
125 		Rule r = rs.getRules().iterator().next();
126 		assertEquals("MockRuleName", r.getName());
127 		assertEquals("net.sourceforge.pmd.lang.rule.MockRule", r.getRuleClass());
128 		assertEquals("avoid the mock rule", r.getMessage());
129 	}
130 
131 	@Test
132 	public void testMultipleRules() throws RuleSetNotFoundException {
133 		RuleSet rs = loadRuleSet(MULTIPLE_RULES);
134 		assertEquals(2, rs.size());
135 		Set<String> expected = new HashSet<String>();
136 		expected.add("MockRuleName1");
137 		expected.add("MockRuleName2");
138 		for (Rule rule : rs.getRules()) {
139 			assertTrue(expected.contains(rule.getName()));
140 		}
141 	}
142 
143 	@Test
144 	public void testSingleRuleWithPriority() throws RuleSetNotFoundException {
145 		assertEquals(RulePriority.MEDIUM, loadFirstRule(PRIORITY).getPriority());
146 	}
147 
148 	@Test
149 	@SuppressWarnings("unchecked")
150 	public void testProps() throws RuleSetNotFoundException {
151 		Rule r = loadFirstRule(PROPERTIES);
152 		assertEquals("bar", r.getProperty((PropertyDescriptor<String>) r.getPropertyDescriptor("fooString")));
153 		assertEquals(new Integer(3), r.getProperty((PropertyDescriptor<Integer>) r.getPropertyDescriptor("fooInt")));
154 		assertTrue(r.getProperty((PropertyDescriptor<Boolean>) r.getPropertyDescriptor("fooBoolean")));
155 		assertEquals(3.0d, r.getProperty((PropertyDescriptor<Double>) r.getPropertyDescriptor("fooDouble")), 0.05);
156 		assertNull(r.getPropertyDescriptor("BuggleFish"));
157 		assertNotSame(r.getDescription().indexOf("testdesc2"), -1);
158 	}
159 
160 	@Test
161 	@SuppressWarnings("unchecked")
162 	public void testXPath() throws RuleSetNotFoundException {
163 		Rule r = loadFirstRule(XPATH);
164 		PropertyDescriptor<String> xpathProperty = (PropertyDescriptor<String>) r.getPropertyDescriptor("xpath");
165 		assertNotNull("xpath property descriptor", xpathProperty);
166 		assertNotSame(r.getProperty(xpathProperty).indexOf(" //Block "), -1);
167 	}
168 
169 	@Test
170 	public void testFacadesOffByDefault() throws RuleSetNotFoundException {
171 		Rule r = loadFirstRule(XPATH);
172 		assertFalse(r.usesDFA());
173 	}
174 
175 	@Test
176 	public void testDFAFlag() throws RuleSetNotFoundException {
177 		assertTrue(loadFirstRule(DFA).usesDFA());
178 	}
179 
180 	@Test
181 	public void testExternalReferenceOverride() throws RuleSetNotFoundException {
182 		Rule r = loadFirstRule(REF_OVERRIDE);
183 		assertEquals("TestNameOverride", r.getName());
184 		assertEquals("Test message override", r.getMessage());
185 		assertEquals("Test description override", r.getDescription());
186 		assertEquals("Test that both example are stored", 2, r.getExamples().size());
187 		assertEquals("Test example override", r.getExamples().get(1));
188 		assertEquals(RulePriority.MEDIUM, r.getPriority());
189 		PropertyDescriptor<?> test2Descriptor = r.getPropertyDescriptor("test2");
190 		assertNotNull("test2 descriptor", test2Descriptor);
191 		assertEquals("override2", r.getProperty(test2Descriptor));
192 		PropertyDescriptor<?> test3Descriptor = r.getPropertyDescriptor("test3");
193 		assertNotNull("test3 descriptor", test3Descriptor);
194 		assertEquals("override3", r.getProperty(test3Descriptor));
195 		PropertyDescriptor<?> test4Descriptor = r.getPropertyDescriptor("test4");
196 		assertNotNull("test3 descriptor", test4Descriptor);
197 		assertEquals("new property", r.getProperty(test4Descriptor));
198 	}
199 
200 	@Test
201 	public void testReferenceInternalToInternal()
202 			throws RuleSetNotFoundException {
203 		RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_INTERNAL);
204 
205 		Rule rule = ruleSet.getRuleByName("MockRuleName");
206 		assertNotNull("Could not find Rule MockRuleName", rule);
207 
208 		Rule ruleRef = ruleSet.getRuleByName("MockRuleNameRef");
209 		assertNotNull("Could not find Rule MockRuleNameRef", ruleRef);
210 	}
211 
212 	@Test
213 	public void testReferenceInternalToInternalChain()
214 			throws RuleSetNotFoundException {
215 		RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_INTERNAL_CHAIN);
216 
217 		Rule rule = ruleSet.getRuleByName("MockRuleName");
218 		assertNotNull("Could not find Rule MockRuleName", rule);
219 
220 		Rule ruleRef = ruleSet.getRuleByName("MockRuleNameRef");
221 		assertNotNull("Could not find Rule MockRuleNameRef", ruleRef);
222 
223 		Rule ruleRefRef = ruleSet.getRuleByName("MockRuleNameRefRef");
224 		assertNotNull("Could not find Rule MockRuleNameRefRef", ruleRefRef);
225 	}
226 
227 	@Test
228 	public void testReferenceInternalToExternal()
229 			throws RuleSetNotFoundException {
230 		RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_EXTERNAL);
231 
232 		Rule rule = ruleSet.getRuleByName("ExternalRefRuleName");
233 		assertNotNull("Could not find Rule ExternalRefRuleName", rule);
234 
235 		Rule ruleRef = ruleSet.getRuleByName("ExternalRefRuleNameRef");
236 		assertNotNull("Could not find Rule ExternalRefRuleNameRef", ruleRef);
237 	}
238 
239 	@Test
240 	public void testReferenceInternalToExternalChain()
241 			throws RuleSetNotFoundException {
242 		RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_EXTERNAL_CHAIN);
243 
244 		Rule rule = ruleSet.getRuleByName("ExternalRefRuleName");
245 		assertNotNull("Could not find Rule ExternalRefRuleName", rule);
246 
247 		Rule ruleRef = ruleSet.getRuleByName("ExternalRefRuleNameRef");
248 		assertNotNull("Could not find Rule ExternalRefRuleNameRef", ruleRef);
249 
250 		Rule ruleRefRef = ruleSet.getRuleByName("ExternalRefRuleNameRefRef");
251 		assertNotNull("Could not find Rule ExternalRefRuleNameRefRef",
252 				ruleRefRef);
253 	}
254 
255 	@Test
256 	public void testReferencePriority() throws RuleSetNotFoundException {
257 		RuleSetFactory rsf = new RuleSetFactory();
258 
259 		rsf.setMinimumPriority(RulePriority.LOW);
260 		RuleSet ruleSet = rsf
261 				.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN));
262 		assertEquals("Number of Rules", 3, ruleSet.getRules().size());
263 		assertNotNull(ruleSet.getRuleByName("MockRuleName"));
264 		assertNotNull(ruleSet.getRuleByName("MockRuleNameRef"));
265 		assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef"));
266 
267 		rsf.setMinimumPriority(RulePriority.MEDIUM_HIGH);
268 		ruleSet = rsf
269 				.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN));
270 		assertEquals("Number of Rules", 2, ruleSet.getRules().size());
271 		assertNotNull(ruleSet.getRuleByName("MockRuleNameRef"));
272 		assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef"));
273 
274 		rsf.setMinimumPriority(RulePriority.HIGH);
275 		ruleSet = rsf
276 				.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN));
277 		assertEquals("Number of Rules", 1, ruleSet.getRules().size());
278 		assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef"));
279 
280 		rsf.setMinimumPriority(RulePriority.LOW);
281 		ruleSet = rsf
282 				.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN));
283 		assertEquals("Number of Rules", 3, ruleSet.getRules().size());
284 		assertNotNull(ruleSet.getRuleByName("ExternalRefRuleName"));
285 		assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef"));
286 		assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef"));
287 
288 		rsf.setMinimumPriority(RulePriority.MEDIUM_HIGH);
289 		ruleSet = rsf
290 				.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN));
291 		assertEquals("Number of Rules", 2, ruleSet.getRules().size());
292 		assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef"));
293 		assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef"));
294 
295 		rsf.setMinimumPriority(RulePriority.HIGH);
296 		ruleSet = rsf
297 				.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN));
298 		assertEquals("Number of Rules", 1, ruleSet.getRules().size());
299 		assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef"));
300 	}
301 
302 	@Test
303 	public void testOverrideMessage() throws RuleSetNotFoundException {
304 		Rule r = loadFirstRule(REF_OVERRIDE_ORIGINAL_NAME);
305 		assertEquals("TestMessageOverride", r.getMessage());
306 	}
307 
308 	@Test
309 	public void testOverrideMessageOneElem() throws RuleSetNotFoundException {
310 		Rule r = loadFirstRule(REF_OVERRIDE_ORIGINAL_NAME_ONE_ELEM);
311 		assertEquals("TestMessageOverride", r.getMessage());
312 	}
313 
314 	@Test(expected = IllegalArgumentException.class)
315 	public void testIncorrectExternalRef() throws IllegalArgumentException,
316 			RuleSetNotFoundException {
317 		loadFirstRule(REF_MISPELLED_XREF);
318 	}
319 
320 	@Test
321 	public void testSetPriority() throws RuleSetNotFoundException {
322 		RuleSetFactory rsf = new RuleSetFactory();
323 		rsf.setMinimumPriority(RulePriority.MEDIUM_HIGH);
324 		assertEquals(0, rsf
325 				.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size());
326 		rsf.setMinimumPriority(RulePriority.MEDIUM_LOW);
327 		assertEquals(1, rsf
328 				.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size());
329 	}
330 
331 	@Test
332 	public void testLanguage() throws RuleSetNotFoundException {
333 		Rule r = loadFirstRule(LANGUAGE);
334 		assertEquals(Language.JAVA, r.getLanguage());
335 	}
336 
337 	@Test(expected = IllegalArgumentException.class)
338 	public void testIncorrectLanguage() throws RuleSetNotFoundException {
339 		loadFirstRule(INCORRECT_LANGUAGE);
340 	}
341 
342 	@Test
343 	public void testMinimumLanugageVersion() throws RuleSetNotFoundException {
344 		Rule r = loadFirstRule(MINIMUM_LANGUAGE_VERSION);
345 		assertEquals(LanguageVersion.JAVA_14, r.getMinimumLanguageVersion());
346 	}
347 
348 	@Test(expected = IllegalArgumentException.class)
349 	public void testIncorrectMinimumLanugageVersion()
350 			throws RuleSetNotFoundException {
351 		loadFirstRule(INCORRECT_MINIMUM_LANGUAGE_VERSION);
352 	}
353 
354 	@Test
355 	public void testMaximumLanugageVersion() throws RuleSetNotFoundException {
356 		Rule r = loadFirstRule(MAXIMUM_LANGUAGE_VERSION);
357 		assertEquals(LanguageVersion.JAVA_17, r.getMaximumLanguageVersion());
358 	}
359 
360 	@Test(expected = IllegalArgumentException.class)
361 	public void testIncorrectMaximumLanugageVersion()
362 			throws RuleSetNotFoundException {
363 		loadFirstRule(INCORRECT_MAXIMUM_LANGUAGE_VERSION);
364 	}
365 
366 	@Test(expected = IllegalArgumentException.class)
367 	public void testInvertedMinimumMaximumLanugageVersions()
368 			throws RuleSetNotFoundException {
369 		loadFirstRule(INVERTED_MINIMUM_MAXIMUM_LANGUAGE_VERSIONS);
370 	}
371 
372 	@Test
373 	public void testDirectDeprecatedRule() throws RuleSetNotFoundException {
374 		Rule r = loadFirstRule(DIRECT_DEPRECATED_RULE);
375 		assertNotNull("Direct Deprecated Rule", r);
376 	}
377 
378 	@Test
379 	public void testReferenceToDeprecatedRule() throws RuleSetNotFoundException {
380 		Rule r = loadFirstRule(REFERENCE_TO_DEPRECATED_RULE);
381 		assertNotNull("Reference to Deprecated Rule", r);
382 		assertTrue("Rule Reference", r instanceof RuleReference);
383 		assertFalse("Not deprecated", r.isDeprecated());
384 		assertTrue("Original Rule Deprecated", ((RuleReference) r).getRule()
385 				.isDeprecated());
386 		assertEquals("Rule name", r.getName(), DEPRECATED_RULE_NAME);
387 	}
388 
389 	@Test
390 	public void testRuleSetReferenceWithDeprecatedRule()
391 			throws RuleSetNotFoundException {
392 		RuleSet ruleSet = loadRuleSet(REFERENCE_TO_RULESET_WITH_DEPRECATED_RULE);
393 		assertNotNull("RuleSet", ruleSet);
394 		assertFalse("RuleSet empty", ruleSet.getRules().isEmpty());
395 		// No deprecated Rules should be loaded when loading an entire RuleSet
396 		// by reference.
397 		Rule r = ruleSet.getRuleByName(DEPRECATED_RULE_NAME);
398 		assertNull("Deprecated Rule Reference", r);
399 		for (Rule rule : ruleSet.getRules()) {
400 			assertFalse("Rule not deprecated", rule.isDeprecated());
401 		}
402 	}
403 
404 	@Test
405 	public void testExternalReferences() throws RuleSetNotFoundException {
406 		RuleSet rs = loadRuleSet(EXTERNAL_REFERENCE_RULE_SET);
407 		assertEquals(1, rs.size());
408 		assertEquals(UnusedLocalVariableRule.class.getName(), rs.getRuleByName(
409 				"UnusedLocalVariable").getRuleClass());
410 	}
411 
412 	@Test
413 	public void testIncludeExcludePatterns() throws RuleSetNotFoundException {
414 		RuleSet ruleSet = loadRuleSet(INCLUDE_EXCLUDE_RULESET);
415 
416 		assertNotNull("Include patterns", ruleSet.getIncludePatterns());
417 		assertEquals("Include patterns size", 2, ruleSet.getIncludePatterns()
418 				.size());
419 		assertEquals("Include pattern #1", "include1", ruleSet
420 				.getIncludePatterns().get(0));
421 		assertEquals("Include pattern #2", "include2", ruleSet
422 				.getIncludePatterns().get(1));
423 
424 		assertNotNull("Exclude patterns", ruleSet.getExcludePatterns());
425 		assertEquals("Exclude patterns size", 3, ruleSet.getExcludePatterns()
426 				.size());
427 		assertEquals("Exclude pattern #1", "exclude1", ruleSet
428 				.getExcludePatterns().get(0));
429 		assertEquals("Exclude pattern #2", "exclude2", ruleSet
430 				.getExcludePatterns().get(1));
431 		assertEquals("Exclude pattern #3", "exclude3", ruleSet
432 				.getExcludePatterns().get(2));
433 	}
434 
435 	@Test
436 	public void testAllPMDBuiltInRulesMeetConventions() throws IOException,
437 			RuleSetNotFoundException, ParserConfigurationException,
438 			SAXException {
439 		int invalidSinceAttributes = 0;
440 		int invalidExternalInfoURL = 0;
441 		int invalidClassName = 0;
442 		int invalidRegexSuppress = 0;
443 		int invalidXPathSuppress = 0;
444 		String messages = "";
445 		// TODO Need to handle each Language
446 		List<String> ruleSetFileNames = getRuleSetFileNames();
447 		for (String fileName : ruleSetFileNames) {
448 			RuleSet ruleSet = loadRuleSetByFileName(fileName);
449 			for (Rule rule : ruleSet.getRules()) {
450 
451 				// Skip references
452 				if (rule instanceof RuleReference) {
453 					continue;
454 				}
455 
456 				Language language = Language.JAVA;
457 				String group = fileName
458 						.substring(fileName.lastIndexOf('/') + 1);
459 				group = group.substring(0, group.indexOf(".xml"));
460 				if (group.indexOf('-') >= 0) {
461 					group = group.substring(0, group.indexOf('-'));
462 				}
463 
464 				// Is since missing ?
465 				if (rule.getSince() == null) {
466 					invalidSinceAttributes++;
467 					messages += "Rule " + fileName + "/" + rule.getName()
468 							+ " is missing 'since' attribute" + PMD.EOL;
469 				}
470 				// Is URL valid ?
471 				if (rule.getExternalInfoUrl() == null
472 						|| "".equalsIgnoreCase(rule.getExternalInfoUrl())) {
473 					invalidExternalInfoURL++;
474 					messages += "Rule " + fileName + "/" + rule.getName()
475 							+ " is missing 'externalInfoURL' attribute"
476 							+ PMD.EOL;
477 				} else {
478 					String expectedExternalInfoURL = "http://pmd.sourceforge.net/.+/rules/"
479 							+ fileName.replaceAll("rulesets/", "").replaceAll(
480 									".xml", "") + ".html#" + rule.getName();
481 					if (rule.getExternalInfoUrl() == null
482 						|| !rule.getExternalInfoUrl().matches(expectedExternalInfoURL)) {
483 						invalidExternalInfoURL++;
484 						messages += "Rule "
485 								+ fileName
486 								+ "/"
487 								+ rule.getName()
488 								+ " seems to have an invalid 'externalInfoURL' value ("
489 								+ rule.getExternalInfoUrl()
490 								+ "), it should be:" + expectedExternalInfoURL
491 								+ PMD.EOL;
492 					}
493 				}
494 				// Proper class name/packaging?
495 				String expectedClassName = "net.sourceforge.pmd.lang."
496 						+ language.getTerseName() + ".rule." + group + "."
497 						+ rule.getName() + "Rule";
498 				if (!rule.getRuleClass().equals(expectedClassName)
499 						&& !rule.getRuleClass().equals(
500 								XPathRule.class.getName())) {
501 					invalidClassName++;
502 					messages += "Rule " + fileName + "/" + rule.getName()
503 							+ " seems to have an invalid 'class' value ("
504 							+ rule.getRuleClass() + "), it should be:"
505 							+ expectedClassName + PMD.EOL;
506 				}
507 				// Should not have violation suppress regex property
508 				if (rule.getProperty(Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR) != null) {
509 					invalidRegexSuppress++;
510 					messages += "Rule "
511 							+ fileName
512 							+ "/"
513 							+ rule.getName()
514 							+ " should not have '"
515 							+ Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR.name()
516 							+ "', this is intended for end user customization only."
517 							+ PMD.EOL;
518 				}
519 				// Should not have violation suppress xpath property
520 				if (rule.getProperty(Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR) != null) {
521 					invalidXPathSuppress++;
522 					messages += "Rule "
523 							+ fileName
524 							+ "/"
525 							+ rule.getName()
526 							+ " should not have '"
527 							+ Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR.name()
528 							+ "', this is intended for end user customization only."
529 							+ PMD.EOL;
530 				}
531 			}
532 		}
533 		// We do this at the end to ensure we test ALL the rules before failing
534 		// the test
535 		if (invalidSinceAttributes > 0 || invalidExternalInfoURL > 0
536 				|| invalidClassName > 0 || invalidRegexSuppress > 0
537 				|| invalidXPathSuppress > 0) {
538 			fail("All built-in PMD rules need 'since' attribute ("
539 					+ invalidSinceAttributes
540 					+ " are missing), a proper ExternalURLInfo ("
541 					+ invalidExternalInfoURL
542 					+ " are invalid), a class name meeting conventions ("
543 					+ invalidClassName + " are invalid), no '"
544 					+ Rule.VIOLATION_SUPPRESS_REGEX_DESCRIPTOR.name()
545 					+ "' property (" + invalidRegexSuppress
546 					+ " are invalid), and no '"
547 					+ Rule.VIOLATION_SUPPRESS_XPATH_DESCRIPTOR.name()
548 					+ "' property (" + invalidXPathSuppress + " are invalid)"
549 					+ PMD.EOL + messages);
550 		}
551 	}
552 
553 	@Test
554 	public void testXmlSchema() throws IOException, RuleSetNotFoundException,
555 			ParserConfigurationException, SAXException {
556 		boolean allValid = true;
557 		List<String> ruleSetFileNames = getRuleSetFileNames();
558 		for (String fileName : ruleSetFileNames) {
559 			boolean valid = validateAgainstSchema(fileName);
560 			allValid = allValid && valid;
561 		}
562 		assertTrue("All XML must parse without producing validation messages.",
563 				allValid);
564 	}
565 
566 	@Test
567 	public void testDtd() throws IOException, RuleSetNotFoundException,
568 			ParserConfigurationException, SAXException {
569 		boolean allValid = true;
570 		List<String> ruleSetFileNames = getRuleSetFileNames();
571 		for (String fileName : ruleSetFileNames) {
572 			boolean valid = validateAgainstDtd(fileName);
573 			allValid = allValid && valid;
574 		}
575 		assertTrue("All XML must parse without producing validation messages.",
576 				allValid);
577 	}
578 
579 	@Test
580 	public void testReadWriteRoundTrip() throws IOException,
581 			RuleSetNotFoundException, ParserConfigurationException,
582 			SAXException {
583 
584 		List<String> ruleSetFileNames = getRuleSetFileNames();
585 		for (String fileName : ruleSetFileNames) {
586 			testRuleSet(fileName);
587 		}
588 	}
589 
590 	public void testRuleSet(String fileName) throws IOException,
591 			RuleSetNotFoundException, ParserConfigurationException,
592 			SAXException {
593 
594 		// Load original XML
595 //		String xml1 = readFullyToString(ResourceLoader.loadResourceAsStream(fileName));
596 //		System.out.println("xml1: " + xml1);
597 
598 		// Load the original RuleSet
599 		RuleSet ruleSet1 = loadRuleSetByFileName(fileName);
600 
601 		// Write to XML, first time
602 		ByteArrayOutputStream outputStream1 = new ByteArrayOutputStream();
603 		RuleSetWriter writer1 = new RuleSetWriter(outputStream1);
604 		writer1.write(ruleSet1);
605 		writer1.close();
606 		String xml2 = new String(outputStream1.toByteArray());
607 		// System.out.println("xml2: " + xml2);
608 
609 		// Read RuleSet from XML, first time
610 		RuleSetFactory ruleSetFactory = new RuleSetFactory();
611 		RuleSet ruleSet2 = ruleSetFactory
612 				.createRuleSet(createRuleSetReferenceId(xml2));
613 
614 		// Do write/read a 2nd time, just to be sure
615 
616 		// Write to XML, second time
617 		ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream();
618 		RuleSetWriter writer2 = new RuleSetWriter(outputStream2);
619 		writer2.write(ruleSet2);
620 		writer2.close();
621 		String xml3 = new String(outputStream2.toByteArray());
622 		// System.out.println("xml3: " + xml3);
623 
624 		// Read RuleSet from XML, second time
625 		RuleSet ruleSet3 = ruleSetFactory
626 				.createRuleSet(createRuleSetReferenceId(xml3));
627 
628 		// The 2 written XMLs should all be valid w.r.t Schema/DTD
629 		assertTrue(
630 				"1st roundtrip RuleSet XML is not valid against Schema (filename: " + fileName + ")",
631 				validateAgainstSchema(new ByteArrayInputStream(xml2.getBytes())));
632 		assertTrue(
633 				"2nd roundtrip RuleSet XML is not valid against Schema (filename: " + fileName + ")",
634 				validateAgainstSchema(new ByteArrayInputStream(xml3.getBytes())));
635 		assertTrue("1st roundtrip RuleSet XML is not valid against DTD (filename: " + fileName + ")",
636 				validateAgainstDtd(new ByteArrayInputStream(xml2.getBytes())));
637 		assertTrue("2nd roundtrip RuleSet XML is not valid against DTD (filename: " + fileName + ")",
638 				validateAgainstDtd(new ByteArrayInputStream(xml3.getBytes())));
639 
640 		// All 3 versions of the RuleSet should be the same
641 		assertEqualsRuleSet(
642 				"Original RuleSet and 1st roundtrip Ruleset not the same (filename: " + fileName + ")",
643 				ruleSet1, ruleSet2);
644 		assertEqualsRuleSet(
645 				"1st roundtrip Ruleset and 2nd roundtrip RuleSet not the same (filename: " + fileName + ")",
646 				ruleSet2, ruleSet3);
647 
648 		// It's hard to compare the XML DOMs. At least the roundtrip ones should
649 		// textually be the same.
650 		assertEquals("1st roundtrip RuleSet XML and 2nd roundtrip RuleSet XML (filename: " + fileName + ")",
651 				xml2, xml3);
652 	}
653 
654 	private void assertEqualsRuleSet(String message, RuleSet ruleSet1,
655 			RuleSet ruleSet2) {
656 		assertEquals(message + ", RuleSet name", ruleSet1.getName(), ruleSet2
657 				.getName());
658 		assertEquals(message + ", RuleSet description", ruleSet1
659 				.getDescription(), ruleSet2.getDescription());
660 		assertEquals(message + ", RuleSet exclude patterns", ruleSet1
661 				.getExcludePatterns(), ruleSet2.getExcludePatterns());
662 		assertEquals(message + ", RuleSet include patterns", ruleSet1
663 				.getIncludePatterns(), ruleSet2.getIncludePatterns());
664 		assertEquals(message + ", RuleSet rule count", ruleSet1.getRules()
665 				.size(), ruleSet2.getRules().size());
666 
667 		for (int i = 0; i < ruleSet1.getRules().size(); i++) {
668 			Rule rule1 = ((List<Rule>) ruleSet1.getRules()).get(i);
669 			Rule rule2 = ((List<Rule>) ruleSet2.getRules()).get(i);
670 
671 			assertFalse(message + ", Different RuleReference",
672 					rule1 instanceof RuleReference
673 							&& !(rule2 instanceof RuleReference)
674 							|| !(rule1 instanceof RuleReference)
675 							&& rule2 instanceof RuleReference);
676 
677 			if (rule1 instanceof RuleReference) {
678 				RuleReference ruleReference1 = (RuleReference) rule1;
679 				RuleReference ruleReference2 = (RuleReference) rule2;
680 				assertEquals(message + ", RuleReference overridden language",
681 						ruleReference1.getOverriddenLanguage(), ruleReference2
682 								.getOverriddenLanguage());
683 				assertEquals(
684 						message
685 								+ ", RuleReference overridden minimum language version",
686 						ruleReference1.getOverriddenMinimumLanguageVersion(),
687 						ruleReference2.getOverriddenMinimumLanguageVersion());
688 				assertEquals(
689 						message
690 								+ ", RuleReference overridden maximum language version",
691 						ruleReference1.getOverriddenMaximumLanguageVersion(),
692 						ruleReference2.getOverriddenMaximumLanguageVersion());
693 				assertEquals(message + ", RuleReference overridden deprecated",
694 						ruleReference1.isOverriddenDeprecated(), ruleReference2
695 								.isOverriddenDeprecated());
696 				assertEquals(message + ", RuleReference overridden name",
697 						ruleReference1.getOverriddenName(), ruleReference2
698 								.getOverriddenName());
699 				assertEquals(
700 						message + ", RuleReference overridden description",
701 						ruleReference1.getOverriddenDescription(),
702 						ruleReference2.getOverriddenDescription());
703 				assertEquals(message + ", RuleReference overridden message",
704 						ruleReference1.getOverriddenMessage(), ruleReference2
705 								.getOverriddenMessage());
706 				assertEquals(message
707 						+ ", RuleReference overridden external info url",
708 						ruleReference1.getOverriddenExternalInfoUrl(),
709 						ruleReference2.getOverriddenExternalInfoUrl());
710 				assertEquals(message + ", RuleReference overridden priority",
711 						ruleReference1.getOverriddenPriority(), ruleReference2
712 								.getOverriddenPriority());
713 				assertEquals(message + ", RuleReference overridden examples",
714 						ruleReference1.getOverriddenExamples(), ruleReference2
715 								.getOverriddenExamples());
716 			}
717 
718 			assertEquals(message + ", Rule name", rule1.getName(), rule2
719 					.getName());
720 			assertEquals(message + ", Rule class", rule1.getRuleClass(), rule2
721 					.getRuleClass());
722 			assertEquals(message + ", Rule description " + rule1.getName(),
723 					rule1.getDescription(), rule2.getDescription());
724 			assertEquals(message + ", Rule message", rule1.getMessage(), rule2
725 					.getMessage());
726 			assertEquals(message + ", Rule external info url", rule1
727 					.getExternalInfoUrl(), rule2.getExternalInfoUrl());
728 			assertEquals(message + ", Rule priority", rule1.getPriority(),
729 					rule2.getPriority());
730 			assertEquals(message + ", Rule examples", rule1.getExamples(),
731 					rule2.getExamples());
732 
733 			List<PropertyDescriptor<?>> propertyDescriptors1 = rule1
734 					.getPropertyDescriptors();
735 			List<PropertyDescriptor<?>> propertyDescriptors2 = rule2
736 					.getPropertyDescriptors();
737 			try {
738 				assertEquals(message + ", Rule property descriptor ",
739 						propertyDescriptors1, propertyDescriptors2);
740 			} catch (Error e) {
741 				throw e;
742 			}
743 			for (int j = 0; j < propertyDescriptors1.size(); j++) {
744 				assertEquals(message + ", Rule property value " + j, rule1
745 						.getProperty(propertyDescriptors1.get(j)), rule2
746 						.getProperty(propertyDescriptors2.get(j)));
747 			}
748 			assertEquals(message + ", Rule property descriptor count",
749 					propertyDescriptors1.size(), propertyDescriptors2.size());
750 		}
751 	}
752 
753 	private boolean validateAgainstSchema(String fileName) throws IOException,
754 			RuleSetNotFoundException, ParserConfigurationException,
755 			SAXException {
756 		InputStream inputStream = loadResourceAsStream(fileName);
757 		boolean valid = validateAgainstSchema(inputStream);
758 		if (!valid) {
759 			System.err.println("Validation against XML Schema failed for: "
760 					+ fileName);
761 		}
762 		return valid;
763 	}
764 
765 	private boolean validateAgainstSchema(InputStream inputStream)
766 			throws IOException, RuleSetNotFoundException,
767 			ParserConfigurationException, SAXException {
768 		
769 		saxParser.parse(inputStream, validateDefaultHandlerXsd.resetValid());
770 		inputStream.close();
771 		return validateDefaultHandlerXsd.isValid();
772 	}
773 
774 	private boolean validateAgainstDtd(String fileName) throws IOException,
775 			RuleSetNotFoundException, ParserConfigurationException,
776 			SAXException {
777 		InputStream inputStream = loadResourceAsStream(fileName);
778 		boolean valid = validateAgainstDtd(inputStream);
779 		if (!valid) {
780 			System.err
781 					.println("Validation against DTD failed for: " + fileName);
782 		}
783 		return valid;
784 	}
785 
786 	private boolean validateAgainstDtd(InputStream inputStream)
787 			throws IOException, RuleSetNotFoundException,
788 			ParserConfigurationException, SAXException {
789 		
790 		// Read file into memory
791 		String file = readFullyToString(inputStream);
792 
793 		// Remove XML Schema stuff, replace with DTD
794 		file = file.replaceAll("<\\?xml [ a-zA-Z0-9=\".-]*\\?>", "");
795 		file = file.replaceAll(
796 				"xmlns=\"" + RuleSetWriter.RULESET_NS_URI + "\"", "");
797 		file = file.replaceAll(
798 				"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"", "");
799 		file = file
800 				.replaceAll(
801 						"xsi:schemaLocation=\"" + RuleSetWriter.RULESET_NS_URI + " http://pmd.sourceforge.net/ruleset_2_0_0.xsd\"",
802 						"");
803 
804 		file = "<?xml version=\"1.0\"?>" + PMD.EOL
805 				+ "<!DOCTYPE ruleset SYSTEM \"file://"
806 				+ System.getProperty("user.dir") + "/src/main/resources/ruleset_2_0_0.dtd\">"
807 				+ PMD.EOL + file;
808 
809 		inputStream = new ByteArrayInputStream(file.getBytes());
810 
811 		saxParser.parse(inputStream, validateDefaultHandlerDtd.resetValid());
812 		inputStream.close();
813 		return validateDefaultHandlerDtd.isValid();
814 	}
815 
816 	private String readFullyToString(InputStream inputStream)
817 			throws IOException {
818 		StringBuilder buf = new StringBuilder(64 * 1024);
819 		BufferedReader reader = new BufferedReader(new InputStreamReader(
820 				inputStream));
821 		String line;
822 		while ((line = reader.readLine()) != null) {
823 			buf.append(line);
824 			buf.append(PMD.EOL);
825 		}
826 		reader.close();
827 		return buf.toString();
828 	}
829 
830 	// Gets all test PMD Ruleset XML files
831 	private List<String> getRuleSetFileNames() throws IOException,
832 			RuleSetNotFoundException {
833 		Properties properties = new Properties();
834 		properties.load(ResourceLoader
835 				.loadResourceAsStream("rulesets/java/rulesets.properties"));
836 		String fileNames = properties.getProperty("rulesets.filenames");
837 		StringTokenizer st = new StringTokenizer(fileNames, ",");
838 		List<String> ruleSetFileNames = new ArrayList<String>();
839 		while (st.hasMoreTokens()) {
840 			ruleSetFileNames.add(st.nextToken());
841 		}
842 		return ruleSetFileNames;
843 	}
844 
845 	private static class ValidateDefaultHandler extends DefaultHandler {
846 		private final String validateDocument;
847 		private boolean valid = true;
848 
849 		public ValidateDefaultHandler(String validateDocument) {
850 			this.validateDocument = validateDocument;
851 		}
852 		
853 		public ValidateDefaultHandler resetValid() {
854 			valid = true;
855 			return this;
856 		}
857 
858 		public boolean isValid() {
859 			return valid;
860 		}
861 
862 		@Override
863 		public void error(SAXParseException e) throws SAXException {
864 			log("Error", e);
865 		}
866 
867 		@Override
868 		public void fatalError(SAXParseException e) throws SAXException {
869 			log("FatalError", e);
870 		}
871 
872 		@Override
873 		public void warning(SAXParseException e) throws SAXException {
874 			log("Warning", e);
875 		}
876 
877 		private void log(String prefix, SAXParseException e) {
878 			String message = prefix + " at (" + e.getLineNumber() + ", " + e.getColumnNumber() + "): " + e.getMessage();
879 			System.err.println(message);
880 			valid = false;
881 		}
882 
883 		@Override
884 		public InputSource resolveEntity(String publicId, String systemId)
885 				throws IOException, SAXException {
886 			if ("http://pmd.sourceforge.net/ruleset_2_0_0.xsd".equals(systemId)
887 					|| systemId.endsWith("ruleset_2_0_0.dtd")) {
888 				try {
889 					InputStream inputStream = loadResourceAsStream(validateDocument);
890 					return new InputSource(inputStream);
891 				} catch (RuleSetNotFoundException e) {
892 					System.err.println(e.getMessage());
893 					throw new IOException(e.getMessage());
894 				}
895 			}
896 			throw new IllegalArgumentException(
897 					"No clue how to handle: publicId=" + publicId
898 							+ ", systemId=" + systemId);
899 		}
900 	}
901 
902 	private static InputStream loadResourceAsStream(String resource)
903 			throws RuleSetNotFoundException {
904 		InputStream inputStream = ResourceLoader.loadResourceAsStream(resource,
905 				RuleSetFactoryTest.class.getClassLoader());
906 		if (inputStream == null) {
907 			throw new RuleSetNotFoundException(
908 					"Can't find resource "
909 							+ resource
910 							+ "  Make sure the resource is a valid file or URL or is on the CLASSPATH.  Here's the current classpath: "
911 							+ System.getProperty("java.class.path"));
912 		}
913 		return inputStream;
914 	}
915 
916 	private static final String REF_OVERRIDE_ORIGINAL_NAME = "<?xml version=\"1.0\"?>"
917 			+ PMD.EOL
918 			+ "<ruleset name=\"test\">"
919 			+ PMD.EOL
920 			+ " <description>testdesc</description>"
921 			+ PMD.EOL
922 			+ " <rule "
923 			+ PMD.EOL
924 			+ "  ref=\"rulesets/java/unusedcode.xml/UnusedLocalVariable\" message=\"TestMessageOverride\"> "
925 			+ PMD.EOL + " </rule>" + PMD.EOL + "</ruleset>";
926 
927 	private static final String REF_MISPELLED_XREF = "<?xml version=\"1.0\"?>"
928 			+ PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
929 			+ " <description>testdesc</description>" + PMD.EOL + " <rule "
930 			+ PMD.EOL
931 			+ "  ref=\"rulesets/java/unusedcode.xml/FooUnusedLocalVariable\"> "
932 			+ PMD.EOL + " </rule>" + PMD.EOL + "</ruleset>";
933 
934 	private static final String REF_OVERRIDE_ORIGINAL_NAME_ONE_ELEM = "<?xml version=\"1.0\"?>"
935 			+ PMD.EOL
936 			+ "<ruleset name=\"test\">"
937 			+ PMD.EOL
938 			+ " <description>testdesc</description>"
939 			+ PMD.EOL
940 			+ " <rule ref=\"rulesets/java/unusedcode.xml/UnusedLocalVariable\" message=\"TestMessageOverride\"/> "
941 			+ PMD.EOL + "</ruleset>";
942 
943 	private static final String REF_OVERRIDE = "<?xml version=\"1.0\"?>"
944 			+ PMD.EOL
945 			+ "<ruleset name=\"test\">"
946 			+ PMD.EOL
947 			+ " <description>testdesc</description>"
948 			+ PMD.EOL
949 			+ " <rule "
950 			+ PMD.EOL
951 			+ "  ref=\"rulesets/java/unusedcode.xml/UnusedLocalVariable\" "
952 			+ PMD.EOL
953 			+ "  name=\"TestNameOverride\" "
954 			+ PMD.EOL
955 			+ "  message=\"Test message override\"> "
956 			+ PMD.EOL
957 			+ "  <description>Test description override</description>"
958 			+ PMD.EOL
959 			+ "  <example>Test example override</example>"
960 			+ PMD.EOL
961 			+ "  <priority>3</priority>"
962 			+ PMD.EOL
963 			+ "  <properties>"
964 			+ PMD.EOL
965 			+ "   <property name=\"test2\" description=\"test2\" type=\"String\" value=\"override2\"/>"
966 			+ PMD.EOL
967 			+ "   <property name=\"test3\" description=\"test3\" type=\"String\"><value>override3</value></property>"
968 			+ PMD.EOL
969 			+ "   <property name=\"test4\" description=\"test4\" type=\"String\" value=\"new property\"/>"
970 			+ PMD.EOL + "  </properties>" + PMD.EOL + " </rule>" + PMD.EOL
971 			+ "</ruleset>";
972 
973 	private static final String REF_INTERNAL_TO_INTERNAL = "<?xml version=\"1.0\"?>"
974 			+ PMD.EOL
975 			+ "<ruleset name=\"test\">"
976 			+ PMD.EOL
977 			+ " <description>testdesc</description>"
978 			+ PMD.EOL
979 			+ "<rule "
980 			+ PMD.EOL
981 			+ "name=\"MockRuleName\" "
982 			+ PMD.EOL
983 			+ "message=\"avoid the mock rule\" "
984 			+ PMD.EOL
985 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\">"
986 			+ PMD.EOL
987 			+ "</rule>"
988 			+ " <rule ref=\"MockRuleName\" name=\"MockRuleNameRef\"/> "
989 			+ PMD.EOL + "</ruleset>";
990 
991 	private static final String REF_INTERNAL_TO_INTERNAL_CHAIN = "<?xml version=\"1.0\"?>"
992 			+ PMD.EOL
993 			+ "<ruleset name=\"test\">"
994 			+ PMD.EOL
995 			+ " <description>testdesc</description>"
996 			+ PMD.EOL
997 			+ "<rule "
998 			+ PMD.EOL
999 			+ "name=\"MockRuleName\" "
1000 			+ PMD.EOL
1001 			+ "message=\"avoid the mock rule\" "
1002 			+ PMD.EOL
1003 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\">"
1004 			+ PMD.EOL
1005 			+ "</rule>"
1006 			+ " <rule ref=\"MockRuleName\" name=\"MockRuleNameRef\"><priority>2</priority></rule> "
1007 			+ PMD.EOL
1008 			+ " <rule ref=\"MockRuleNameRef\" name=\"MockRuleNameRefRef\"><priority>1</priority></rule> "
1009 			+ PMD.EOL + "</ruleset>";
1010 
1011 	private static final String REF_INTERNAL_TO_EXTERNAL = "<?xml version=\"1.0\"?>"
1012 			+ PMD.EOL
1013 			+ "<ruleset name=\"test\">"
1014 			+ PMD.EOL
1015 			+ " <description>testdesc</description>"
1016 			+ PMD.EOL
1017 			+ "<rule "
1018 			+ PMD.EOL
1019 			+ "name=\"ExternalRefRuleName\" "
1020 			+ PMD.EOL
1021 			+ "ref=\"rulesets/java/unusedcode.xml/UnusedLocalVariable\"/>"
1022 			+ PMD.EOL
1023 			+ " <rule ref=\"ExternalRefRuleName\" name=\"ExternalRefRuleNameRef\"/> "
1024 			+ PMD.EOL + "</ruleset>";
1025 
1026 	private static final String REF_INTERNAL_TO_EXTERNAL_CHAIN = "<?xml version=\"1.0\"?>"
1027 			+ PMD.EOL
1028 			+ "<ruleset name=\"test\">"
1029 			+ PMD.EOL
1030 			+ " <description>testdesc</description>"
1031 			+ PMD.EOL
1032 			+ "<rule "
1033 			+ PMD.EOL
1034 			+ "name=\"ExternalRefRuleName\" "
1035 			+ PMD.EOL
1036 			+ "ref=\"rulesets/java/unusedcode.xml/UnusedLocalVariable\"/>"
1037 			+ PMD.EOL
1038 			+ " <rule ref=\"ExternalRefRuleName\" name=\"ExternalRefRuleNameRef\"><priority>2</priority></rule> "
1039 			+ PMD.EOL
1040 			+ " <rule ref=\"ExternalRefRuleNameRef\" name=\"ExternalRefRuleNameRefRef\"><priority>1</priority></rule> "
1041 			+ PMD.EOL + "</ruleset>";
1042 
1043 	private static final String EMPTY_RULESET = "<?xml version=\"1.0\"?>"
1044 			+ PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
1045 			+ "<description>testdesc</description>" + PMD.EOL + "</ruleset>";
1046 
1047 	private static final String SINGLE_RULE = "<?xml version=\"1.0\"?>"
1048 			+ PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
1049 			+ "<description>testdesc</description>" + PMD.EOL + "<rule "
1050 			+ PMD.EOL + "name=\"MockRuleName\" " + PMD.EOL
1051 			+ "message=\"avoid the mock rule\" " + PMD.EOL
1052 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\">"
1053 			+ "<priority>3</priority>" + PMD.EOL + "</rule></ruleset>";
1054 
1055 	private static final String MULTIPLE_RULES = "<?xml version=\"1.0\"?>"
1056 			+ PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
1057 			+ "<description>testdesc</description>" + PMD.EOL
1058 			+ "<rule name=\"MockRuleName1\" " + PMD.EOL
1059 			+ "message=\"avoid the mock rule\" " + PMD.EOL
1060 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\">" + PMD.EOL
1061 			+ "</rule>" + PMD.EOL + "<rule name=\"MockRuleName2\" " + PMD.EOL
1062 			+ "message=\"avoid the mock rule\" " + PMD.EOL
1063 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\">" + PMD.EOL
1064 			+ "</rule></ruleset>";
1065 
1066 	private static final String PROPERTIES = "<?xml version=\"1.0\"?>"
1067 			+ PMD.EOL
1068 			+ "<ruleset name=\"test\">"
1069 			+ PMD.EOL
1070 			+ "<description>testdesc</description>"
1071 			+ PMD.EOL
1072 			+ "<rule name=\"MockRuleName\" "
1073 			+ PMD.EOL
1074 			+ "message=\"avoid the mock rule\" "
1075 			+ PMD.EOL
1076 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\">"
1077 			+ PMD.EOL
1078 			+ "<description>testdesc2</description>"
1079 			+ PMD.EOL
1080 			+ "<properties>"
1081 			+ PMD.EOL
1082 			+ "<property name=\"fooBoolean\" description=\"test\" type=\"Boolean\" value=\"true\" />"
1083 			+ PMD.EOL
1084 			+ "<property name=\"fooChar\" description=\"test\" type=\"Character\" value=\"B\" />"
1085 			+ PMD.EOL
1086 			+ "<property name=\"fooInt\" description=\"test\" type=\"Integer\" min=\"1\" max=\"10\" value=\"3\" />"
1087 			+ PMD.EOL
1088 			+ "<property name=\"fooFloat\" description=\"test\" type=\"Float\" min=\"1.0\" max=\"1.0\" value=\"1.0\"  />"
1089 			+ PMD.EOL
1090 			+ "<property name=\"fooDouble\" description=\"test\" type=\"Double\" min=\"1.0\" max=\"9.0\" value=\"3.0\"  />"
1091 			+ PMD.EOL
1092 			+ "<property name=\"fooString\" description=\"test\" type=\"String\" value=\"bar\" />"
1093 			+ PMD.EOL + "</properties>" + PMD.EOL + "</rule></ruleset>";
1094 
1095 	private static final String XPATH = "<?xml version=\"1.0\"?>" + PMD.EOL
1096 			+ "<ruleset name=\"test\">" + PMD.EOL
1097 			+ "<description>testdesc</description>" + PMD.EOL
1098 			+ "<rule name=\"MockRuleName\" " + PMD.EOL
1099 			+ "message=\"avoid the mock rule\" " + PMD.EOL
1100 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\">"
1101 			+ "<priority>3</priority>" + PMD.EOL + PMD.EOL
1102 			+ "<description>testdesc2</description>" + PMD.EOL + "<properties>"
1103 			+ PMD.EOL
1104 			+ "<property name=\"xpath\" description=\"test\" type=\"String\">"
1105 			+ PMD.EOL + "<value>" + PMD.EOL + "<![CDATA[ //Block ]]>" + PMD.EOL
1106 			+ "</value>" + PMD.EOL + "</property>" + PMD.EOL + "</properties>"
1107 			+ PMD.EOL + "</rule></ruleset>";
1108 
1109 	private static final String PRIORITY = "<?xml version=\"1.0\"?>" + PMD.EOL
1110 			+ "<ruleset name=\"test\">" + PMD.EOL
1111 			+ "<description>testdesc</description>" + PMD.EOL + "<rule "
1112 			+ PMD.EOL + "name=\"MockRuleName\" " + PMD.EOL
1113 			+ "message=\"avoid the mock rule\" " + PMD.EOL
1114 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\">"
1115 			+ "<priority>3</priority>" + PMD.EOL + "</rule></ruleset>";
1116 
1117 	private static final String LANGUAGE = "<?xml version=\"1.0\"?>"
1118 			+ PMD.EOL
1119 			+ "<ruleset name=\"test\">"
1120 			+ PMD.EOL
1121 			+ "<description>testdesc</description>"
1122 			+ PMD.EOL
1123 			+ "<rule "
1124 			+ PMD.EOL
1125 			+ "name=\"MockRuleName\" "
1126 			+ PMD.EOL
1127 			+ "message=\"avoid the mock rule\" "
1128 			+ PMD.EOL
1129 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\" language=\"java\">"
1130 			+ PMD.EOL + "</rule></ruleset>";
1131 
1132 	private static final String INCORRECT_LANGUAGE = "<?xml version=\"1.0\"?>"
1133 			+ PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
1134 			+ "<description>testdesc</description>" + PMD.EOL + "<rule "
1135 			+ PMD.EOL + "name=\"MockRuleName\" " + PMD.EOL
1136 			+ "message=\"avoid the mock rule\" " + PMD.EOL
1137 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\"" + PMD.EOL
1138 			+ " language=\"bogus\">" + PMD.EOL + "</rule></ruleset>";
1139 
1140 	private static final String MINIMUM_LANGUAGE_VERSION = "<?xml version=\"1.0\"?>"
1141 			+ PMD.EOL
1142 			+ "<ruleset name=\"test\">"
1143 			+ PMD.EOL
1144 			+ "<description>testdesc</description>"
1145 			+ PMD.EOL
1146 			+ "<rule "
1147 			+ PMD.EOL
1148 			+ "name=\"MockRuleName\" "
1149 			+ PMD.EOL
1150 			+ "message=\"avoid the mock rule\" "
1151 			+ PMD.EOL
1152 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\""
1153 			+ PMD.EOL
1154 			+ " language=\"java\""
1155 			+ PMD.EOL
1156 			+ " minimumLanguageVersion=\"1.4\">"
1157 			+ PMD.EOL
1158 			+ "</rule></ruleset>";
1159 
1160 	private static final String INCORRECT_MINIMUM_LANGUAGE_VERSION = "<?xml version=\"1.0\"?>"
1161 			+ PMD.EOL
1162 			+ "<ruleset name=\"test\">"
1163 			+ PMD.EOL
1164 			+ "<description>testdesc</description>"
1165 			+ PMD.EOL
1166 			+ "<rule "
1167 			+ PMD.EOL
1168 			+ "name=\"MockRuleName\" "
1169 			+ PMD.EOL
1170 			+ "message=\"avoid the mock rule\" "
1171 			+ PMD.EOL
1172 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\""
1173 			+ PMD.EOL
1174 			+ " language=\"java\""
1175 			+ PMD.EOL
1176 			+ " minimumLanguageVersion=\"bogus\">"
1177 			+ PMD.EOL
1178 			+ "</rule></ruleset>";
1179 
1180 	private static final String MAXIMUM_LANGUAGE_VERSION = "<?xml version=\"1.0\"?>"
1181 			+ PMD.EOL
1182 			+ "<ruleset name=\"test\">"
1183 			+ PMD.EOL
1184 			+ "<description>testdesc</description>"
1185 			+ PMD.EOL
1186 			+ "<rule "
1187 			+ PMD.EOL
1188 			+ "name=\"MockRuleName\" "
1189 			+ PMD.EOL
1190 			+ "message=\"avoid the mock rule\" "
1191 			+ PMD.EOL
1192 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\""
1193 			+ PMD.EOL
1194 			+ " language=\"java\""
1195 			+ PMD.EOL
1196 			+ " maximumLanguageVersion=\"1.7\">"
1197 			+ PMD.EOL
1198 			+ "</rule></ruleset>";
1199 
1200 	private static final String INCORRECT_MAXIMUM_LANGUAGE_VERSION = "<?xml version=\"1.0\"?>"
1201 			+ PMD.EOL
1202 			+ "<ruleset name=\"test\">"
1203 			+ PMD.EOL
1204 			+ "<description>testdesc</description>"
1205 			+ PMD.EOL
1206 			+ "<rule "
1207 			+ PMD.EOL
1208 			+ "name=\"MockRuleName\" "
1209 			+ PMD.EOL
1210 			+ "message=\"avoid the mock rule\" "
1211 			+ PMD.EOL
1212 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\""
1213 			+ PMD.EOL
1214 			+ " language=\"java\""
1215 			+ PMD.EOL
1216 			+ " maximumLanguageVersion=\"bogus\">"
1217 			+ PMD.EOL
1218 			+ "</rule></ruleset>";
1219 
1220 	private static final String INVERTED_MINIMUM_MAXIMUM_LANGUAGE_VERSIONS = "<?xml version=\"1.0\"?>"
1221 			+ PMD.EOL
1222 			+ "<ruleset name=\"test\">"
1223 			+ PMD.EOL
1224 			+ "<description>testdesc</description>"
1225 			+ PMD.EOL
1226 			+ "<rule "
1227 			+ PMD.EOL
1228 			+ "name=\"MockRuleName\" "
1229 			+ PMD.EOL
1230 			+ "message=\"avoid the mock rule\" "
1231 			+ PMD.EOL
1232 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\" "
1233 			+ PMD.EOL
1234 			+ "language=\"java\""
1235 			+ PMD.EOL
1236 			+ " minimumLanguageVersion=\"1.7\""
1237 			+ PMD.EOL
1238 			+ "maximumLanguageVersion=\"1.4\">"
1239 			+ PMD.EOL
1240 			+ "</rule></ruleset>";
1241 
1242 	private static final String DIRECT_DEPRECATED_RULE = "<?xml version=\"1.0\"?>"
1243 			+ PMD.EOL
1244 			+ "<ruleset name=\"test\">"
1245 			+ PMD.EOL
1246 			+ "<description>testdesc</description>"
1247 			+ PMD.EOL
1248 			+ "<rule "
1249 			+ PMD.EOL
1250 			+ "name=\"MockRuleName\" "
1251 			+ PMD.EOL
1252 			+ "message=\"avoid the mock rule\" "
1253 			+ PMD.EOL
1254 			+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\" deprecated=\"true\">"
1255 			+ PMD.EOL + "</rule></ruleset>";
1256 
1257 	// Note: Update this RuleSet name to a different RuleSet with deprecated
1258 	// Rules when the Rules are finally removed.
1259 	private static final String DEPRECATED_RULE_RULESET_NAME = "rulesets/java/basic.xml";
1260 
1261 	// Note: Update this Rule name to a different deprecated Rule when the one
1262 	// listed here is finally removed.
1263 	private static final String DEPRECATED_RULE_NAME = "EmptyCatchBlock";
1264 
1265 	private static final String REFERENCE_TO_DEPRECATED_RULE = "<?xml version=\"1.0\"?>"
1266 			+ PMD.EOL
1267 			+ "<ruleset name=\"test\">"
1268 			+ PMD.EOL
1269 			+ "<description>testdesc</description>"
1270 			+ PMD.EOL
1271 			+ "<rule "
1272 			+ PMD.EOL
1273 			+ "ref=\""
1274 			+ DEPRECATED_RULE_RULESET_NAME
1275 			+ "/"
1276 			+ DEPRECATED_RULE_NAME + "\">" + PMD.EOL + "</rule></ruleset>";
1277 
1278 	private static final String REFERENCE_TO_RULESET_WITH_DEPRECATED_RULE = "<?xml version=\"1.0\"?>"
1279 			+ PMD.EOL
1280 			+ "<ruleset name=\"test\">"
1281 			+ PMD.EOL
1282 			+ "<description>testdesc</description>"
1283 			+ PMD.EOL
1284 			+ "<rule "
1285 			+ PMD.EOL
1286 			+ "ref=\""
1287 			+ DEPRECATED_RULE_RULESET_NAME
1288 			+ "\">"
1289 			+ PMD.EOL + "</rule></ruleset>";
1290 
1291 	private static final String DFA = "<?xml version=\"1.0\"?>" + PMD.EOL
1292 			+ "<ruleset name=\"test\">" + PMD.EOL
1293 			+ "<description>testdesc</description>" + PMD.EOL + "<rule "
1294 			+ PMD.EOL + "name=\"MockRuleName\" " + PMD.EOL
1295 			+ "message=\"avoid the mock rule\" " + PMD.EOL + "dfa=\"true\" "
1296 			+ PMD.EOL + "class=\"net.sourceforge.pmd.lang.rule.MockRule\">"
1297 			+ "<priority>3</priority>" + PMD.EOL + "</rule></ruleset>";
1298 
1299 	private static final String INCLUDE_EXCLUDE_RULESET = "<?xml version=\"1.0\"?>"
1300 			+ PMD.EOL
1301 			+ "<ruleset name=\"test\">"
1302 			+ PMD.EOL
1303 			+ "<description>testdesc</description>"
1304 			+ PMD.EOL
1305 			+ "<include-pattern>include1</include-pattern>"
1306 			+ PMD.EOL
1307 			+ "<include-pattern>include2</include-pattern>"
1308 			+ PMD.EOL
1309 			+ "<exclude-pattern>exclude1</exclude-pattern>"
1310 			+ PMD.EOL
1311 			+ "<exclude-pattern>exclude2</exclude-pattern>"
1312 			+ PMD.EOL
1313 			+ "<exclude-pattern>exclude3</exclude-pattern>"
1314 			+ PMD.EOL
1315 			+ "</ruleset>";
1316 
1317 	private static final String EXTERNAL_REFERENCE_RULE_SET = "<?xml version=\"1.0\"?>"
1318 			+ PMD.EOL
1319 			+ "<ruleset name=\"test\">"
1320 			+ PMD.EOL
1321 			+ "<description>testdesc</description>"
1322 			+ PMD.EOL
1323 			+ "<rule ref=\"rulesets/java/unusedcode.xml/UnusedLocalVariable\"/>"
1324 			+ PMD.EOL + "</ruleset>";
1325 
1326 	private Rule loadFirstRule(String ruleSetXml)
1327 			throws RuleSetNotFoundException {
1328 		RuleSet rs = loadRuleSet(ruleSetXml);
1329 		return rs.getRules().iterator().next();
1330 	}
1331 
1332 	private RuleSet loadRuleSetByFileName(String ruleSetFileName)
1333 			throws RuleSetNotFoundException {
1334 		RuleSetFactory rsf = new RuleSetFactory();
1335 		return rsf.createRuleSet(ruleSetFileName);
1336 	}
1337 
1338 	private RuleSet loadRuleSet(String ruleSetXml)
1339 			throws RuleSetNotFoundException {
1340 		RuleSetFactory rsf = new RuleSetFactory();
1341 		return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml));
1342 	}
1343 
1344 	private static RuleSetReferenceId createRuleSetReferenceId(
1345 			final String ruleSetXml) {
1346 		return new RuleSetReferenceId(null) {
1347 			@Override
1348 			public InputStream getInputStream(ClassLoader classLoader)
1349 					throws RuleSetNotFoundException {
1350 				return new ByteArrayInputStream(ruleSetXml.getBytes());
1351 			}
1352 		};
1353 	}
1354 
1355 	public static junit.framework.Test suite() {
1356 		return new JUnit4TestAdapter(RuleSetFactoryTest.class);
1357 	}
1358 
1359 }