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 }