001: /**
002: * <copyright>
003: * Copyright 1997-2002 InfoEther, LLC
004: * under sponsorship of the Defense Advanced Research Projects Agency
005: (DARPA).
006: *
007: * This program is free software; you can redistribute it and/or modify
008: * it under the terms of the Cougaar Open Source License as published
009: by
010: * DARPA on the Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THE COUGAAR SOFTWARE AND ANY DERIVATIVE SUPPLIED BY LICENSOR IS
013: * PROVIDED 'AS IS' WITHOUT WARRANTIES OF ANY KIND, WHETHER EXPRESS OR
014: * IMPLIED, INCLUDING (BUT NOT LIMITED TO) ALL IMPLIED WARRANTIES OF
015: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND WITHOUT
016: * ANY WARRANTIES AS TO NON-INFRINGEMENT. IN NO EVENT SHALL COPYRIGHT
017: * HOLDER BE LIABLE FOR ANY DIRECT, SPECIAL, INDIRECT OR CONSEQUENTIAL
018: * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE OF DATA OR PROFITS,
019: * TORTIOUS CONDUCT, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
020: * PERFORMANCE OF THE COUGAAR SOFTWARE.
021: * </copyright>
022: */package test.net.sourceforge.pmd;
023:
024: import static org.junit.Assert.assertEquals;
025: import static org.junit.Assert.assertFalse;
026: import static org.junit.Assert.assertNotNull;
027: import static org.junit.Assert.assertNull;
028: import static org.junit.Assert.assertTrue;
029:
030: import java.io.BufferedReader;
031: import java.io.ByteArrayInputStream;
032: import java.io.ByteArrayOutputStream;
033: import java.io.IOException;
034: import java.io.InputStream;
035: import java.io.InputStreamReader;
036: import java.util.ArrayList;
037: import java.util.HashSet;
038: import java.util.Iterator;
039: import java.util.List;
040: import java.util.Properties;
041: import java.util.Set;
042: import java.util.StringTokenizer;
043:
044: import javax.xml.parsers.ParserConfigurationException;
045: import javax.xml.parsers.SAXParser;
046: import javax.xml.parsers.SAXParserFactory;
047:
048: import junit.framework.JUnit4TestAdapter;
049: import net.sourceforge.pmd.PMD;
050: import net.sourceforge.pmd.Rule;
051: import net.sourceforge.pmd.RuleReference;
052: import net.sourceforge.pmd.RuleSet;
053: import net.sourceforge.pmd.RuleSetFactory;
054: import net.sourceforge.pmd.RuleSetNotFoundException;
055: import net.sourceforge.pmd.RuleSetWriter;
056: import net.sourceforge.pmd.rules.UnusedLocalVariableRule;
057: import net.sourceforge.pmd.util.ResourceLoader;
058:
059: import org.junit.Before;
060: import org.junit.Test;
061: import org.xml.sax.InputSource;
062: import org.xml.sax.SAXException;
063: import org.xml.sax.SAXParseException;
064: import org.xml.sax.helpers.DefaultHandler;
065:
066: import test.net.sourceforge.pmd.testframework.TestDescriptor;
067:
068: public class RuleSetFactoryTest {
069:
070: private boolean isJdk14;
071:
072: @Before
073: public void setUp() {
074: try {
075: Class.forName("java.lang.Appendable");
076: } catch (Throwable t) {
077: isJdk14 = true;
078: }
079: }
080:
081: @Test
082: public void testRuleSetFileName() throws RuleSetNotFoundException {
083: RuleSet rs = loadRuleSet(EMPTY_RULESET);
084: assertNull("RuleSet file name not expected", rs.getFileName());
085:
086: RuleSetFactory rsf = new RuleSetFactory();
087: rs = rsf.createSingleRuleSet("rulesets/basic.xml");
088: assertEquals("wrong RuleSet file name", rs.getFileName(),
089: "rulesets/basic.xml");
090: }
091:
092: @Test
093: public void testNoRuleSetFileName() {
094: RuleSet rs = loadRuleSet(EMPTY_RULESET);
095: assertNull("RuleSet file name not expected", rs.getFileName());
096: }
097:
098: @Test
099: public void testRefs() throws Throwable {
100: InputStream in = ResourceLoader.loadResourceAsStream(
101: "rulesets/favorites.xml", this .getClass()
102: .getClassLoader());
103: if (in == null) {
104: throw new RuleSetNotFoundException(
105: "Can't find resource Make sure the resource is a valid file or URL or is on the CLASSPATH. Here's the current classpath: "
106: + System.getProperty("java.class.path"));
107: }
108: RuleSetFactory rsf = new RuleSetFactory();
109: RuleSet rs = rsf.createSingleRuleSet("rulesets/favorites.xml");
110: assertNotNull(rs.getRuleByName("WhileLoopsMustUseBraces"));
111: }
112:
113: @Test(expected=RuleSetNotFoundException.class)
114: public void testRuleSetNotFound() throws RuleSetNotFoundException {
115: RuleSetFactory rsf = new RuleSetFactory();
116: rsf.createSingleRuleSet("fooooo");
117: }
118:
119: @Test
120: public void testCreateEmptyRuleSet() {
121: RuleSet rs = loadRuleSet(EMPTY_RULESET);
122: assertEquals("test", rs.getName());
123: assertEquals(0, rs.size());
124: }
125:
126: @Test
127: public void testSingleRule() {
128: RuleSet rs = loadRuleSet(SINGLE_RULE);
129: assertEquals(1, rs.size());
130: Rule r = rs.getRules().iterator().next();
131: assertEquals("MockRuleName", r.getName());
132: assertEquals("net.sourceforge.pmd.MockRule", r.getRuleClass());
133: assertEquals("avoid the mock rule", r.getMessage());
134: }
135:
136: @Test
137: public void testMultipleRules() {
138: RuleSet rs = loadRuleSet(MULTIPLE_RULES);
139: assertEquals(2, rs.size());
140: Set<String> expected = new HashSet<String>();
141: expected.add("MockRuleName1");
142: expected.add("MockRuleName2");
143: for (Iterator<Rule> i = rs.getRules().iterator(); i.hasNext();) {
144: assertTrue(expected.contains(i.next().getName()));
145: }
146: }
147:
148: @Test
149: public void testSingleRuleWithPriority() {
150: assertEquals(3, loadFirstRule(PRIORITY).getPriority());
151: }
152:
153: @Test
154: public void testProps() {
155: Rule r = loadFirstRule(PROPERTIES);
156: assertTrue(r.hasProperty("foo"));
157: assertEquals("bar", r.getStringProperty("foo"));
158: assertEquals(2, r.getIntProperty("fooint"));
159: assertTrue(r.hasProperty("fooBoolean"));
160: assertTrue(r.getBooleanProperty("fooBoolean"));
161: assertTrue(r.hasProperty("fooDouble"));
162: assertEquals(1.0, r.getDoubleProperty("fooDouble"), 0.05);
163: assertTrue(!r.hasProperty("BuggleFish"));
164: assertTrue(r.getDescription().indexOf("testdesc2") != -1);
165: }
166:
167: @Test
168: public void testXPathPluginnameProperty() {
169: Rule r = loadFirstRule(XPATH_PLUGINNAME);
170: assertTrue(r.hasProperty("pluginname"));
171: }
172:
173: @Test
174: public void testXPath() {
175: Rule r = loadFirstRule(XPATH);
176: assertTrue(r.hasProperty("xpath"));
177: assertTrue(r.getStringProperty("xpath").indexOf(" //Block ") != -1);
178: }
179:
180: @Test
181: public void testFacadesOffByDefault() {
182: Rule r = loadFirstRule(XPATH);
183: assertFalse(r.usesDFA());
184: }
185:
186: @Test
187: public void testDFAFlag() {
188: assertTrue(loadFirstRule(DFA).usesDFA());
189: }
190:
191: @Test
192: public void testExternalReferenceOverride() {
193: Rule r = loadFirstRule(REF_OVERRIDE);
194: assertEquals("TestNameOverride", r.getName());
195: assertEquals("Test message override", r.getMessage());
196: assertEquals("Test description override", r.getDescription());
197: assertEquals("Test example override", r.getExample());
198: assertEquals("Test that both example are stored", 2, r
199: .getExamples().size());
200: assertEquals(3, r.getPriority());
201: assertTrue(r.hasProperty("test2"));
202: assertEquals("override2", r.getStringProperty("test2"));
203: assertTrue(r.hasProperty("test3"));
204: assertEquals("override3", r.getStringProperty("test3"));
205: assertTrue(r.hasProperty("test4"));
206: assertEquals("new property", r.getStringProperty("test4"));
207: }
208:
209: @Test
210: public void testOverrideMessage() {
211: Rule r = loadFirstRule(REF_OVERRIDE_ORIGINAL_NAME);
212: assertEquals("TestMessageOverride", r.getMessage());
213: }
214:
215: @Test
216: public void testOverrideMessageOneElem() {
217: Rule r = loadFirstRule(REF_OVERRIDE_ORIGINAL_NAME_ONE_ELEM);
218: assertEquals("TestMessageOverride", r.getMessage());
219: }
220:
221: @Test(expected=IllegalArgumentException.class)
222: public void testExternalRef() throws IllegalArgumentException {
223: loadFirstRule(REF_MISPELLED_XREF);
224: }
225:
226: @Test
227: public void testSetPriority() {
228: RuleSetFactory rsf = new RuleSetFactory();
229: rsf.setMinimumPriority(2);
230: assertEquals(0, rsf.createRuleSet(
231: new ByteArrayInputStream(SINGLE_RULE.getBytes()))
232: .size());
233: rsf.setMinimumPriority(4);
234: assertEquals(1, rsf.createRuleSet(
235: new ByteArrayInputStream(SINGLE_RULE.getBytes()))
236: .size());
237: }
238:
239: @Test
240: public void testIncludeExcludePatterns() {
241: RuleSet ruleSet = loadRuleSet(INCLUDE_EXCLUDE_RULESET);
242:
243: assertNotNull("Include patterns", ruleSet.getIncludePatterns());
244: assertEquals("Include patterns size", 2, ruleSet
245: .getIncludePatterns().size());
246: assertEquals("Include pattern #1", "include1", ruleSet
247: .getIncludePatterns().get(0));
248: assertEquals("Include pattern #2", "include2", ruleSet
249: .getIncludePatterns().get(1));
250:
251: assertNotNull("Exclude patterns", ruleSet.getExcludePatterns());
252: assertEquals("Exclude patterns size", 3, ruleSet
253: .getExcludePatterns().size());
254: assertEquals("Exclude pattern #1", "exclude1", ruleSet
255: .getExcludePatterns().get(0));
256: assertEquals("Exclude pattern #2", "exclude2", ruleSet
257: .getExcludePatterns().get(1));
258: assertEquals("Exclude pattern #3", "exclude3", ruleSet
259: .getExcludePatterns().get(2));
260: }
261:
262: @Test
263: public void testAllPMDBuiltInRulesNeedSince() throws IOException,
264: RuleSetNotFoundException, ParserConfigurationException,
265: SAXException {
266: boolean allValid = true;
267: List<String> ruleSetFileNames = getRuleSetFileNames();
268: for (String fileName : ruleSetFileNames) {
269: RuleSet ruleSet = loadRuleSetByFileName(fileName);
270: for (Rule rule : ruleSet.getRules()) {
271: if (rule.getSince() == null) {
272: allValid = false;
273: System.err.println("Rule " + fileName + "/"
274: + rule.getName()
275: + " is missing 'since' attribute");
276: }
277: }
278: }
279: assertTrue("All built-in PMD rules need 'since' attribute.",
280: allValid);
281: }
282:
283: // FUTURE Enable this test when we're ready to rename rules
284: /*
285: @Test
286: public void testAllPMDBuiltInRulesShouldEndWithRule() throws IOException, RuleSetNotFoundException,
287: ParserConfigurationException, SAXException {
288: boolean allValid = true;
289: List<String> ruleSetFileNames = getRuleSetFileNames();
290: for (String fileName : ruleSetFileNames) {
291: RuleSet ruleSet = loadRuleSetByFileName(fileName);
292: for (Rule rule : ruleSet.getRules()) {
293: if (!rule.getRuleClass().endsWith("Rule")) {
294: allValid = false;
295: System.err.println("Rule " + fileName + "/" + rule.getName()
296: + " does not have 'ruleClass' that ends with 'Rule': " + rule.getRuleClass());
297: }
298: }
299: }
300: assertTrue("All built-in PMD rule classes should end with 'Rule'.", allValid);
301: }
302: */
303:
304: @Test
305: public void testXmlSchema() throws IOException,
306: RuleSetNotFoundException, ParserConfigurationException,
307: SAXException {
308: if (isJdk14) {
309: // ignore failure with jdk 1.4
310: return;
311: }
312:
313: boolean allValid = true;
314: List<String> ruleSetFileNames = getRuleSetFileNames();
315: for (String fileName : ruleSetFileNames) {
316: boolean valid = validateAgainstSchema(fileName);
317: allValid = allValid && valid;
318: }
319: assertTrue(
320: "All XML must parse without producing validation messages.",
321: allValid);
322: }
323:
324: @Test
325: public void testDtd() throws IOException, RuleSetNotFoundException,
326: ParserConfigurationException, SAXException {
327: boolean allValid = true;
328: List<String> ruleSetFileNames = getRuleSetFileNames();
329: for (String fileName : ruleSetFileNames) {
330: boolean valid = validateAgainstDtd(fileName);
331: allValid = allValid && valid;
332: }
333: assertTrue(
334: "All XML must parse without producing validation messages.",
335: allValid);
336: }
337:
338: @Test
339: public void testReadWriteRoundTrip() throws IOException,
340: RuleSetNotFoundException, ParserConfigurationException,
341: SAXException {
342:
343: List<String> ruleSetFileNames = getRuleSetFileNames();
344: for (String fileName : ruleSetFileNames) {
345: testRuleSet(fileName);
346: }
347: }
348:
349: @Test
350: public void testWindowsJdk14Bug() throws IOException,
351: RuleSetNotFoundException, ParserConfigurationException,
352: SAXException {
353:
354: if (TestDescriptor.inRegressionTestMode()) {
355: // skip this test if we're only running regression tests
356: return;
357: }
358: // This fails only on Windows running the weaved pmd version
359: testRuleSet("regress/test/net/sourceforge/pmd/xml/j2ee.xml");
360: }
361:
362: public void testRuleSet(String fileName) throws IOException,
363: RuleSetNotFoundException, ParserConfigurationException,
364: SAXException {
365:
366: // Load original XML
367: String xml1 = readFullyToString(ResourceLoader
368: .loadResourceAsStream(fileName));
369:
370: // Load the original RuleSet
371: RuleSet ruleSet1 = loadRuleSetByFileName(fileName);
372:
373: // Write to XML, first time
374: ByteArrayOutputStream outputStream1 = new ByteArrayOutputStream();
375: RuleSetWriter writer1 = new RuleSetWriter(outputStream1);
376: writer1.write(ruleSet1);
377: writer1.close();
378: String xml2 = new String(outputStream1.toByteArray());
379:
380: // Read RuleSet from XML, first time
381: RuleSetFactory ruleSetFactory = new RuleSetFactory();
382: RuleSet ruleSet2 = ruleSetFactory
383: .createRuleSet(new ByteArrayInputStream(outputStream1
384: .toByteArray()));
385:
386: // Do write/read a 2nd time, just to be sure
387:
388: // Write to XML, second time
389: ByteArrayOutputStream outputStream2 = new ByteArrayOutputStream();
390: RuleSetWriter writer2 = new RuleSetWriter(outputStream2);
391: writer2.write(ruleSet2);
392: writer2.close();
393: String xml3 = new String(outputStream2.toByteArray());
394:
395: // System.out.println("xml1: " + xml1);
396: // System.out.println("xml2: " + xml2);
397: // System.out.println("xml3: " + xml3);
398:
399: // Read RuleSet from XML, second time
400: RuleSet ruleSet3 = ruleSetFactory
401: .createRuleSet(new ByteArrayInputStream(outputStream2
402: .toByteArray()));
403:
404: // The 2 written XMLs should all be valid w.r.t Schema/DTD
405: if (!isJdk14) {
406: assertTrue(
407: "1st roundtrip RuleSet XML is not valid against Schema",
408: validateAgainstSchema(new ByteArrayInputStream(xml2
409: .getBytes())));
410: assertTrue(
411: "2nd roundtrip RuleSet XML is not valid against Schema",
412: validateAgainstSchema(new ByteArrayInputStream(xml3
413: .getBytes())));
414: }
415: assertTrue(
416: "1st roundtrip RuleSet XML is not valid against DTD",
417: validateAgainstDtd(new ByteArrayInputStream(xml2
418: .getBytes())));
419: assertTrue(
420: "2nd roundtrip RuleSet XML is not valid against DTD",
421: validateAgainstDtd(new ByteArrayInputStream(xml3
422: .getBytes())));
423:
424: // All 3 versions of the RuleSet should be the same
425: assertEqualsRuleSet(
426: "Original RuleSet and 1st roundtrip Ruleset not the same",
427: ruleSet1, ruleSet2);
428: assertEqualsRuleSet(
429: "1st roundtrip Ruleset and 2nd roundtrip RuleSet not the same",
430: ruleSet2, ruleSet3);
431:
432: // It's hard to compare the XML DOMs. At least the roundtrip ones should textually be the same.
433: assertEquals(
434: "1st roundtrip RuleSet XML and 2nd roundtrip RuleSet XML",
435: xml2, xml3);
436: }
437:
438: private void assertEqualsRuleSet(String message, RuleSet ruleSet1,
439: RuleSet ruleSet2) {
440: assertEquals(message + ", RuleSet name", ruleSet1.getName(),
441: ruleSet2.getName());
442: assertEquals(message + ", RuleSet description", ruleSet1
443: .getDescription(), ruleSet2.getDescription());
444: assertEquals(message + ", RuleSet language", ruleSet1
445: .getLanguage(), ruleSet2.getLanguage());
446: assertEquals(message + ", RuleSet exclude patterns", ruleSet1
447: .getExcludePatterns(), ruleSet2.getExcludePatterns());
448: assertEquals(message + ", RuleSet include patterns", ruleSet1
449: .getIncludePatterns(), ruleSet2.getIncludePatterns());
450: assertEquals(message + ", RuleSet rule count", ruleSet1
451: .getRules().size(), ruleSet2.getRules().size());
452:
453: for (int i = 0; i < ruleSet1.getRules().size(); i++) {
454: Rule rule1 = ((List<Rule>) ruleSet1.getRules()).get(i);
455: Rule rule2 = ((List<Rule>) ruleSet2.getRules()).get(i);
456:
457: assertFalse(
458: message + ", Different RuleReference",
459: ((rule1 instanceof RuleReference) && !(rule2 instanceof RuleReference))
460: || (!(rule1 instanceof RuleReference) && (rule2 instanceof RuleReference)));
461:
462: if (rule1 instanceof RuleReference) {
463: RuleReference ruleReference1 = (RuleReference) rule1;
464: RuleReference ruleReference2 = (RuleReference) rule2;
465: assertEquals(message
466: + ", RuleReference overridden name",
467: ruleReference1.getOverriddenName(),
468: ruleReference2.getOverriddenName());
469: assertEquals(message
470: + ", RuleReference overridden description",
471: ruleReference1.getOverriddenDescription(),
472: ruleReference2.getOverriddenDescription());
473: assertEquals(message
474: + ", RuleReference overridden message",
475: ruleReference1.getOverriddenMessage(),
476: ruleReference2.getOverriddenMessage());
477: assertEquals(
478: message
479: + ", RuleReference overridden external info url",
480: ruleReference1.getOverriddenExternalInfoUrl(),
481: ruleReference2.getOverriddenExternalInfoUrl());
482: assertEquals(message
483: + ", RuleReference overridden priority",
484: ruleReference1.getOverriddenPriority(),
485: ruleReference2.getOverriddenPriority());
486: assertEquals(message
487: + ", RuleReference overridden examples",
488: ruleReference1.getOverriddenExamples(),
489: ruleReference2.getOverriddenExamples());
490: assertEquals(message
491: + ", RuleReference overridden properties",
492: ruleReference1.getOverriddenProperties(),
493: ruleReference2.getOverriddenProperties());
494: }
495:
496: assertEquals(message + ", Rule name", rule1.getName(),
497: rule2.getName());
498: assertEquals(message + ", Rule class",
499: rule1.getRuleClass(), rule2.getRuleClass());
500: assertEquals(message + ", Rule description", rule1
501: .getDescription(), rule2.getDescription());
502: assertEquals(message + ", Rule message",
503: rule1.getMessage(), rule2.getMessage());
504: assertEquals(message + ", Rule external info url", rule1
505: .getExternalInfoUrl(), rule2.getExternalInfoUrl());
506: assertEquals(message + ", Rule priority", rule1
507: .getPriority(), rule2.getPriority());
508: assertEquals(message + ", Rule examples", rule1
509: .getExamples(), rule2.getExamples());
510: assertEquals(message + ", Rule properties", rule1
511: .getProperties(), rule2.getProperties());
512: }
513: }
514:
515: private boolean validateAgainstSchema(String fileName)
516: throws IOException, RuleSetNotFoundException,
517: ParserConfigurationException, SAXException {
518: InputStream inputStream = loadResourceAsStream(fileName);
519: boolean valid = validateAgainstSchema(inputStream);
520: if (!valid) {
521: System.err
522: .println("Validation against XML Schema failed for: "
523: + fileName);
524: }
525: return valid;
526: }
527:
528: private boolean validateAgainstSchema(InputStream inputStream)
529: throws IOException, RuleSetNotFoundException,
530: ParserConfigurationException, SAXException {
531: SAXParserFactory saxParserFactory = SAXParserFactory
532: .newInstance();
533: saxParserFactory.setValidating(true);
534: saxParserFactory.setNamespaceAware(true);
535:
536: // Hope we're using Xerces, or this may not work!
537: // Note: Features are listed here http://xerces.apache.org/xerces2-j/features.html
538: saxParserFactory.setFeature(
539: "http://xml.org/sax/features/validation", true);
540: saxParserFactory.setFeature(
541: "http://apache.org/xml/features/validation/schema",
542: true);
543: saxParserFactory
544: .setFeature(
545: "http://apache.org/xml/features/validation/schema-full-checking",
546: true);
547:
548: SAXParser saxParser = saxParserFactory.newSAXParser();
549: ValidateDefaultHandler validateDefaultHandler = new ValidateDefaultHandler(
550: "etc/ruleset_xml_schema.xsd");
551: saxParser.parse(inputStream, validateDefaultHandler);
552: inputStream.close();
553: return validateDefaultHandler.isValid();
554: }
555:
556: private boolean validateAgainstDtd(String fileName)
557: throws IOException, RuleSetNotFoundException,
558: ParserConfigurationException, SAXException {
559: InputStream inputStream = loadResourceAsStream(fileName);
560: boolean valid = validateAgainstDtd(inputStream);
561: if (!valid) {
562: System.err.println("Validation against DTD failed for: "
563: + fileName);
564: }
565: return valid;
566: }
567:
568: private boolean validateAgainstDtd(InputStream inputStream)
569: throws IOException, RuleSetNotFoundException,
570: ParserConfigurationException, SAXException {
571: SAXParserFactory saxParserFactory = SAXParserFactory
572: .newInstance();
573: saxParserFactory.setValidating(true);
574: saxParserFactory.setNamespaceAware(true);
575:
576: // Read file into memory
577: String file = readFullyToString(inputStream);
578:
579: // Remove XML Schema stuff, replace with DTD
580: file = file.replaceAll("<\\?xml [ a-zA-Z0-9=\".-]*\\?>", "");
581: file = file.replaceAll(
582: "xmlns=\"http://pmd.sf.net/ruleset/1.0.0\"", "");
583: file = file
584: .replaceAll(
585: "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"",
586: "");
587: file = file
588: .replaceAll(
589: "xsi:schemaLocation=\"http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd\"",
590: "");
591: file = file
592: .replaceAll(
593: "xsi:noNamespaceSchemaLocation=\"http://pmd.sf.net/ruleset_xml_schema.xsd\"",
594: "");
595:
596: file = "<?xml version=\"1.0\"?>" + PMD.EOL
597: + "<!DOCTYPE ruleset SYSTEM \"file://"
598: + System.getProperty("user.dir")
599: + "/etc/ruleset.dtd\">" + PMD.EOL + file;
600:
601: inputStream = new ByteArrayInputStream(file.getBytes());
602:
603: SAXParser saxParser = saxParserFactory.newSAXParser();
604: ValidateDefaultHandler validateDefaultHandler = new ValidateDefaultHandler(
605: "etc/ruleset.dtd");
606: saxParser.parse(inputStream, validateDefaultHandler);
607: inputStream.close();
608: return validateDefaultHandler.isValid();
609: }
610:
611: private String readFullyToString(InputStream inputStream)
612: throws IOException {
613: StringBuffer buf = new StringBuffer(64 * 1024);
614: BufferedReader reader = new BufferedReader(
615: new InputStreamReader(inputStream));
616: String line;
617: while ((line = reader.readLine()) != null) {
618: buf.append(line);
619: buf.append(PMD.EOL);
620: }
621: reader.close();
622: return buf.toString();
623: }
624:
625: // Gets all test PMD Ruleset XML files
626: private List<String> getRuleSetFileNames() throws IOException,
627: RuleSetNotFoundException {
628: Properties properties = new Properties();
629: properties.load(ResourceLoader
630: .loadResourceAsStream("rulesets/rulesets.properties"));
631: String fileNames = properties.getProperty("rulesets.testnames");
632: StringTokenizer st = new StringTokenizer(fileNames, ",");
633: List<String> ruleSetFileNames = new ArrayList<String>();
634: while (st.hasMoreTokens()) {
635: ruleSetFileNames.add(st.nextToken());
636: }
637: return ruleSetFileNames;
638: }
639:
640: private class ValidateDefaultHandler extends DefaultHandler {
641: private final String validateDocument;
642: private boolean valid = true;
643:
644: public ValidateDefaultHandler(String validateDocument) {
645: this .validateDocument = validateDocument;
646: }
647:
648: public boolean isValid() {
649: return valid;
650: }
651:
652: public void error(SAXParseException e) throws SAXException {
653: log("Error", e);
654: }
655:
656: public void fatalError(SAXParseException e) throws SAXException {
657: log("FatalError", e);
658: }
659:
660: public void warning(SAXParseException e) throws SAXException {
661: log("Warning", e);
662: }
663:
664: private void log(String prefix, SAXParseException e) {
665: String message = prefix + " at (" + e.getLineNumber()
666: + ", " + e.getColumnNumber() + "): "
667: + e.getMessage();
668: System.err.println(message);
669: valid = false;
670: }
671:
672: public InputSource resolveEntity(String publicId,
673: String systemId) throws IOException, SAXException {
674: if ("http://pmd.sf.net/ruleset_xml_schema.xsd"
675: .equals(systemId)
676: || systemId.endsWith("ruleset.dtd")) {
677: try {
678: InputStream inputStream = loadResourceAsStream(validateDocument);
679: return new InputSource(inputStream);
680: } catch (RuleSetNotFoundException e) {
681: System.err.println(e.getMessage());
682: throw new IOException(e.getMessage());
683: }
684: } else {
685: throw new IllegalArgumentException(
686: "No clue how to handle: publicId=" + publicId
687: + ", systemId=" + systemId);
688: }
689: }
690: }
691:
692: private InputStream loadResourceAsStream(String resource)
693: throws RuleSetNotFoundException {
694: InputStream inputStream = ResourceLoader.loadResourceAsStream(
695: resource, this .getClass().getClassLoader());
696: if (inputStream == null) {
697: throw new RuleSetNotFoundException(
698: "Can't find resource "
699: + resource
700: + " Make sure the resource is a valid file or URL or is on the CLASSPATH. Here's the current classpath: "
701: + System.getProperty("java.class.path"));
702: }
703: return inputStream;
704: }
705:
706: private static final String REF_OVERRIDE_ORIGINAL_NAME = "<?xml version=\"1.0\"?>"
707: + PMD.EOL
708: + "<ruleset name=\"test\">"
709: + PMD.EOL
710: + " <description>testdesc</description>"
711: + PMD.EOL
712: + " <rule "
713: + PMD.EOL
714: + " ref=\"rulesets/unusedcode.xml/UnusedLocalVariable\" message=\"TestMessageOverride\"> "
715: + PMD.EOL + " </rule>" + PMD.EOL + "</ruleset>";
716:
717: private static final String REF_MISPELLED_XREF = "<?xml version=\"1.0\"?>"
718: + PMD.EOL
719: + "<ruleset name=\"test\">"
720: + PMD.EOL
721: + " <description>testdesc</description>"
722: + PMD.EOL
723: + " <rule "
724: + PMD.EOL
725: + " ref=\"rulesets/unusedcode.xml/FooUnusedLocalVariable\"> "
726: + PMD.EOL + " </rule>" + PMD.EOL + "</ruleset>";
727:
728: private static final String REF_OVERRIDE_ORIGINAL_NAME_ONE_ELEM = "<?xml version=\"1.0\"?>"
729: + PMD.EOL
730: + "<ruleset name=\"test\">"
731: + PMD.EOL
732: + " <description>testdesc</description>"
733: + PMD.EOL
734: + " <rule ref=\"rulesets/unusedcode.xml/UnusedLocalVariable\" message=\"TestMessageOverride\"/> "
735: + PMD.EOL + "</ruleset>";
736:
737: private static final String REF_OVERRIDE = "<?xml version=\"1.0\"?>"
738: + PMD.EOL
739: + "<ruleset name=\"test\">"
740: + PMD.EOL
741: + " <description>testdesc</description>"
742: + PMD.EOL
743: + " <rule "
744: + PMD.EOL
745: + " ref=\"rulesets/unusedcode.xml/UnusedLocalVariable\" "
746: + PMD.EOL
747: + " name=\"TestNameOverride\" "
748: + PMD.EOL
749: + " message=\"Test message override\"> "
750: + PMD.EOL
751: + " <description>Test description override</description>"
752: + PMD.EOL
753: + " <example>Test example override</example>"
754: + PMD.EOL
755: + " <priority>3</priority>"
756: + PMD.EOL
757: + " <properties>"
758: + PMD.EOL
759: + " <property name=\"test2\" value=\"override2\"/>"
760: + PMD.EOL
761: + " <property name=\"test3\"><value>override3</value></property>"
762: + PMD.EOL
763: + " <property name=\"test4\" value=\"new property\"/>"
764: + PMD.EOL
765: + " </properties>"
766: + PMD.EOL
767: + " </rule>"
768: + PMD.EOL + "</ruleset>";
769:
770: private static final String EMPTY_RULESET = "<?xml version=\"1.0\"?>"
771: + PMD.EOL
772: + "<ruleset name=\"test\">"
773: + PMD.EOL
774: + "<description>testdesc</description>"
775: + PMD.EOL
776: + "</ruleset>";
777:
778: private static final String SINGLE_RULE = "<?xml version=\"1.0\"?>"
779: + PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
780: + "<description>testdesc</description>" + PMD.EOL
781: + "<rule " + PMD.EOL + "name=\"MockRuleName\" " + PMD.EOL
782: + "message=\"avoid the mock rule\" " + PMD.EOL
783: + "class=\"net.sourceforge.pmd.MockRule\">"
784: + "<priority>3</priority>" + PMD.EOL + "</rule></ruleset>";
785:
786: private static final String MULTIPLE_RULES = "<?xml version=\"1.0\"?>"
787: + PMD.EOL
788: + "<ruleset name=\"test\">"
789: + PMD.EOL
790: + "<description>testdesc</description>"
791: + PMD.EOL
792: + "<rule name=\"MockRuleName1\" "
793: + PMD.EOL
794: + "message=\"avoid the mock rule\" "
795: + PMD.EOL
796: + "class=\"net.sourceforge.pmd.MockRule\">"
797: + PMD.EOL
798: + "</rule>"
799: + PMD.EOL
800: + "<rule name=\"MockRuleName2\" "
801: + PMD.EOL
802: + "message=\"avoid the mock rule\" "
803: + PMD.EOL
804: + "class=\"net.sourceforge.pmd.MockRule\">"
805: + PMD.EOL
806: + "</rule></ruleset>";
807:
808: private static final String PROPERTIES = "<?xml version=\"1.0\"?>"
809: + PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
810: + "<description>testdesc</description>" + PMD.EOL
811: + "<rule name=\"MockRuleName\" " + PMD.EOL
812: + "message=\"avoid the mock rule\" " + PMD.EOL
813: + "class=\"net.sourceforge.pmd.MockRule\">" + PMD.EOL
814: + "<description>testdesc2</description>" + PMD.EOL
815: + "<properties>" + PMD.EOL
816: + "<property name=\"fooBoolean\" value=\"true\"/>"
817: + PMD.EOL + "<property name=\"fooDouble\" value=\"1.0\" />"
818: + PMD.EOL + "<property name=\"foo\" value=\"bar\"/>"
819: + PMD.EOL + "<property name=\"fooint\" value=\"2\"/>"
820: + PMD.EOL + "</properties>" + PMD.EOL + "</rule></ruleset>";
821:
822: private static final String XPATH = "<?xml version=\"1.0\"?>"
823: + PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
824: + "<description>testdesc</description>" + PMD.EOL
825: + "<priority>3</priority>" + PMD.EOL
826: + "<rule name=\"MockRuleName\" " + PMD.EOL
827: + "message=\"avoid the mock rule\" " + PMD.EOL
828: + "class=\"net.sourceforge.pmd.MockRule\">" + PMD.EOL
829: + "<description>testdesc2</description>" + PMD.EOL
830: + "<properties>" + PMD.EOL + "<property name=\"xpath\">"
831: + PMD.EOL + "<value>" + PMD.EOL + "<![CDATA[ //Block ]]>"
832: + PMD.EOL + "</value>" + PMD.EOL + "</property>" + PMD.EOL
833: + "</properties>" + PMD.EOL + "</rule></ruleset>";
834:
835: private static final String XPATH_PLUGINNAME = "<?xml version=\"1.0\"?>"
836: + PMD.EOL
837: + "<ruleset name=\"test\">"
838: + PMD.EOL
839: + "<description>testdesc</description>"
840: + PMD.EOL
841: + "<priority>3</priority>"
842: + PMD.EOL
843: + "<rule name=\"MockRuleName\" "
844: + PMD.EOL
845: + "message=\"avoid the mock rule\" "
846: + PMD.EOL
847: + "class=\"net.sourceforge.pmd.MockRule\">"
848: + PMD.EOL
849: + "<description>testdesc2</description>"
850: + PMD.EOL
851: + "<properties>"
852: + PMD.EOL
853: + "<property name=\"xpath\" pluginname=\"true\">"
854: + PMD.EOL
855: + "<value>"
856: + PMD.EOL
857: + "<![CDATA[ //Block ]]>"
858: + PMD.EOL
859: + "</value>"
860: + PMD.EOL
861: + "</property>"
862: + PMD.EOL
863: + "</properties>" + PMD.EOL + "</rule></ruleset>";
864:
865: private static final String PRIORITY = "<?xml version=\"1.0\"?>"
866: + PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
867: + "<description>testdesc</description>" + PMD.EOL
868: + "<rule " + PMD.EOL + "name=\"MockRuleName\" " + PMD.EOL
869: + "message=\"avoid the mock rule\" " + PMD.EOL
870: + "class=\"net.sourceforge.pmd.MockRule\">"
871: + "<priority>3</priority>" + PMD.EOL + "</rule></ruleset>";
872:
873: private static final String DFA = "<?xml version=\"1.0\"?>"
874: + PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
875: + "<description>testdesc</description>" + PMD.EOL
876: + "<rule " + PMD.EOL + "name=\"MockRuleName\" " + PMD.EOL
877: + "message=\"avoid the mock rule\" " + PMD.EOL
878: + "dfa=\"true\" " + PMD.EOL
879: + "class=\"net.sourceforge.pmd.MockRule\">"
880: + "<priority>3</priority>" + PMD.EOL + "</rule></ruleset>";
881:
882: private static final String INCLUDE_EXCLUDE_RULESET = "<?xml version=\"1.0\"?>"
883: + PMD.EOL
884: + "<ruleset name=\"test\">"
885: + PMD.EOL
886: + "<description>testdesc</description>"
887: + PMD.EOL
888: + "<include-pattern>include1</include-pattern>"
889: + PMD.EOL
890: + "<include-pattern>include2</include-pattern>"
891: + PMD.EOL
892: + "<exclude-pattern>exclude1</exclude-pattern>"
893: + PMD.EOL
894: + "<exclude-pattern>exclude2</exclude-pattern>"
895: + PMD.EOL
896: + "<exclude-pattern>exclude3</exclude-pattern>"
897: + PMD.EOL
898: + "</ruleset>";
899:
900: private Rule loadFirstRule(String ruleSetXml) {
901: RuleSet rs = loadRuleSet(ruleSetXml);
902: return rs.getRules().iterator().next();
903: }
904:
905: private RuleSet loadRuleSetByFileName(String ruleSetFileName)
906: throws RuleSetNotFoundException {
907: RuleSetFactory rsf = new RuleSetFactory();
908: return rsf.createSingleRuleSet(ruleSetFileName);
909: }
910:
911: private RuleSet loadRuleSet(String ruleSetXml) {
912: RuleSetFactory rsf = new RuleSetFactory();
913: return rsf.createRuleSet(new ByteArrayInputStream(ruleSetXml
914: .getBytes()));
915: }
916:
917: @Test
918: public void testExternalReferences() {
919: RuleSet rs = loadRuleSet(EXTERNAL_REFERENCE_RULE_SET);
920: assertEquals(1, rs.size());
921: assertEquals(UnusedLocalVariableRule.class.getName(), rs
922: .getRuleByName("UnusedLocalVariable").getRuleClass());
923: }
924:
925: private static final String EXTERNAL_REFERENCE_RULE_SET = "<?xml version=\"1.0\"?>"
926: + PMD.EOL
927: + "<ruleset name=\"test\">"
928: + PMD.EOL
929: + "<description>testdesc</description>"
930: + PMD.EOL
931: + "<rule ref=\"rulesets/unusedcode.xml/UnusedLocalVariable\"/>"
932: + PMD.EOL + "</ruleset>";
933:
934: public static junit.framework.Test suite() {
935: return new JUnit4TestAdapter(RuleSetFactoryTest.class);
936: }
937: }
|