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