Add 'deprecated' attribute to Rules, to facilitate the PMD project reorganizing Rules in a backward compatible fashion without breaking user's RuleSets, while at the same time giving the user notice of an impending change (e.g. fix your RuleSet now, or face an error in the future). Practically speaking this feature can be used to rename Rules as well as indicate they are going to be removed.

Note: Untested possible scenarios are (1) deprecated reference chains (e.g. multiple renames/moves) (2) rename within the same RuleSet.  Address these when they come up.

The RuleSetFactory has had the most changes made to it.  Changes include:
1) Warning messages are logged when using deprecated Rules by reference from the top most user specific RuleSet.
2) When loading an entire RuleSet by reference, deprecated Rules are dropped automatically. In some sense this is backward incompatible in terms of behavior, but it's the best choice.
3) Rework some of the loading mechanics to ensure the ClassLoader is propagated appropriately.  Also added warnings to make sure folks know not to use certain defaulting ClassLoader methods are not used in the internal implementation, and verified they are not currently used.
4) Performance optimization is made to cache intermediate RuleSets to avoid reloading them repeatedly.


git-svn-id: https://pmd.svn.sourceforge.net/svnroot/pmd/trunk@6248 51baf565-9d33-0410-a72c-fc3788e3496d
This commit is contained in:
Ryan Gustafson committed 2008-06-23 05:02:07 +00:00
1 parent 98fcb7a92c
commit 029503348d
15 files changed
+279 -66

No files matched your search

+4
View File
@@ -259,6 +259,8 @@ The following is relatively complete list of the major changes (this may not be
Added - LanguageVersion Rule.getMinimumLanguageVersion()
Added - Rule.setMaximumLanguageVersion(LanguageVersion)
Added - LanguageVersion Rule.getMaximumLanguageVersion()
Added - Rule.setDeprecated(boolean)
Added - boolean Rule.isDeprecated()
API Change - Changes to PMD class
Renamed - PMD.EXCLUDE_MARKER to PMD.SUPPRESS_MARKER
@@ -272,6 +274,7 @@ The following is relatively complete list of the major changes (this may not be
Moved - PMD.setClassLoader(ClassLoader) to Configuration.getClassLoader(ClassLoader)
Moved - PMD.setDefaultLanguageVersion(LanguageVersion) to Configuration.setDefaultLanguageVersion(LanguageVersion)
Moved - PMD.setDefaultLanguageVersions(List<LanguageVersion>) to Configuration.setDefaultLanguageVersions(List<LanguageVersion>)
Moved - PMD.createClasspathClassLoader(String) to Configuration.createClasspathClassLoader(String)
API Change - Changes to Node interface
Renamed - findChildrenOfType() as findDescendantsOfType()
@@ -286,6 +289,7 @@ New Language 'xml' added, for writing XPathRules against XML documents
Rules can now define a 'violationSuppressRegex' property to universally suppress violations with messages matching the given regular expression
Rules can now define a 'violationSuppressXPath' property to universally suppress violations on nodes which match the given relative XPath expression
Rules are now directly associated with a corresponding Language, and a can also be associated with a specific Language Version range if desired.
Rules can now be flagged with deprecated='true' in the RuleSet XML to allow the PMD Project to indicate a Rule (1) is scheduled for removal, (2) has been removed, or (3) has been renamed/moved.
Other changes:
+1
View File
@@ -16,6 +16,7 @@
<!ATTLIST rule class NMTOKEN #IMPLIED >
<!ATTLIST rule dfa CDATA #IMPLIED >
<!ATTLIST rule typeResolution CDATA #IMPLIED >
<!ATTLIST rule deprecated CDATA #IMPLIED >
<!ELEMENT example ( #PCDATA ) >
<!ELEMENT priority ( #PCDATA ) >
<!ELEMENT properties ( property+ ) >
+1
View File
@@ -55,6 +55,7 @@
<xs:attribute name="class" type="xs:NMTOKEN" use="optional" />
<xs:attribute name="dfa" type="xs:boolean" use="optional" /> <!-- rule uses dataflow analysis -->
<xs:attribute name="typeResolution" type="xs:boolean" default="false" use="optional" />
<xs:attribute name="deprecated" type="xs:boolean" default="false" use="optional" />
</xs:complexType>
</xs:element>
@@ -39,6 +39,7 @@ public class RuleReferenceTest {
ruleReference.setLanguage(Language.JAVA);
ruleReference.setMinimumLanguageVersion(LanguageVersion.JAVA_13);
ruleReference.setMaximumLanguageVersion(LanguageVersion.JAVA_17);
ruleReference.setDeprecated(true);
ruleReference.setName("name2");
ruleReference.addProperty("property1", "value2");
ruleReference.setMessage("message2");
@@ -56,6 +57,10 @@ public class RuleReferenceTest {
assertEquals("Override failed", LanguageVersion.JAVA_17, ruleReference.getMaximumLanguageVersion());
assertEquals("Override failed", LanguageVersion.JAVA_17, ruleReference.getOverriddenMaximumLanguageVersion());
assertEquals("Override failed", false, ruleReference.getRule().isDeprecated());
assertEquals("Override failed", true, ruleReference.isDeprecated());
assertEquals("Override failed", true, ruleReference.isOverriddenDeprecated());
assertEquals("Override failed", "name2", ruleReference.getName());
assertEquals("Override failed", "name2", ruleReference.getOverriddenName());
@@ -101,6 +106,7 @@ public class RuleReferenceTest {
ruleReference.setLanguage(Language.JAVA);
ruleReference.setMinimumLanguageVersion(LanguageVersion.JAVA_13);
ruleReference.setMaximumLanguageVersion(LanguageVersion.JAVA_17);
ruleReference.setDeprecated(false);
ruleReference.setName("name1");
ruleReference.addProperty("property1", "value1");
ruleReference.setMessage("message1");
@@ -118,6 +124,9 @@ public class RuleReferenceTest {
assertEquals("Override failed", LanguageVersion.JAVA_17, ruleReference.getMaximumLanguageVersion());
assertNull("Override failed", ruleReference.getOverriddenMaximumLanguageVersion());
assertEquals("Override failed", false, ruleReference.isDeprecated());
assertNull("Override failed", ruleReference.isOverriddenDeprecated());
assertEquals("Override failed", "name1", ruleReference.getName());
assertNull("Override failed", ruleReference.getOverriddenName());
@@ -254,6 +254,35 @@ public class RuleSetFactoryTest {
loadFirstRule(INVERTED_MINIMUM_MAXIMUM_LANGUAGE_VERSIONS);
}
@Test
public void testDirectDeprecatedRule() {
Rule r = loadFirstRule(DIRECT_DEPRECATED_RULE);
assertNotNull("Direct Deprecated Rule", r);
}
@Test
public void testReferenceToDeprecatedRule() {
Rule r = loadFirstRule(REFERENCE_TO_DEPRECATED_RULE);
assertNotNull("Reference to Deprecated Rule", r);
assertTrue("Rule Reference", r instanceof RuleReference);
assertFalse("Not deprecated", r.isDeprecated());
assertTrue("Original Rule Deprecated", ((RuleReference)r).getRule().isDeprecated());
assertEquals("Rule name", r.getName(), DEPRECATED_RULE_NAME);
}
@Test
public void testRuleSetReferenceWithDeprecatedRule() {
RuleSet ruleSet = loadRuleSet(REFERENCE_TO_RULESET_WITH_DEPRECATED_RULE);
assertNotNull("RuleSet", ruleSet);
assertFalse("RuleSet empty", ruleSet.getRules().isEmpty());
// No deprecated Rules should be loaded when loading an entire RuleSet by reference.
Rule r = ruleSet.getRuleByName(DEPRECATED_RULE_NAME);
assertNull("Deprecated Rule Reference", r);
for (Rule rule: ruleSet.getRules()) {
assertFalse("Rule not deprecated", rule.isDeprecated());
}
}
@Test
public void testIncludeExcludePatterns() {
RuleSet ruleSet = loadRuleSet(INCLUDE_EXCLUDE_RULESET);
@@ -478,6 +507,14 @@ public class RuleSetFactoryTest {
if (rule1 instanceof RuleReference) {
RuleReference ruleReference1 = (RuleReference) rule1;
RuleReference ruleReference2 = (RuleReference) rule2;
assertEquals(message + ", RuleReference overridden language", ruleReference1.getOverriddenLanguage(),
ruleReference2.getOverriddenLanguage());
assertEquals(message + ", RuleReference overridden minimum language version", ruleReference1.getOverriddenMinimumLanguageVersion(),
ruleReference2.getOverriddenMinimumLanguageVersion());
assertEquals(message + ", RuleReference overridden maximum language version", ruleReference1.getOverriddenMaximumLanguageVersion(),
ruleReference2.getOverriddenMaximumLanguageVersion());
assertEquals(message + ", RuleReference overridden deprecated", ruleReference1.isOverriddenDeprecated(),
ruleReference2.isOverriddenDeprecated());
assertEquals(message + ", RuleReference overridden name", ruleReference1.getOverriddenName(),
ruleReference2.getOverriddenName());
assertEquals(message + ", RuleReference overridden description", ruleReference1
@@ -774,6 +811,28 @@ public class RuleSetFactoryTest {
+ " minimumLanguageVersion=\"1.7\"" + PMD.EOL + "maximumLanguageVersion=\"1.4\">" + PMD.EOL
+ "</rule></ruleset>";
private static final String DIRECT_DEPRECATED_RULE = "<?xml version=\"1.0\"?>" + PMD.EOL
+ "<ruleset name=\"test\">" + PMD.EOL + "<description>testdesc</description>" + PMD.EOL
+ "<rule "
+ PMD.EOL + "name=\"MockRuleName\" " + PMD.EOL + "message=\"avoid the mock rule\" " + PMD.EOL
+ "class=\"net.sourceforge.pmd.lang.rule.MockRule\" deprecated=\"true\">" + PMD.EOL
+ "</rule></ruleset>";
// Note: Update this RuleSet name to a different RuleSet with deprecated Rules when the Rules are finally removed.
private static final String DEPRECATED_RULE_RULESET_NAME = "rulesets/basic.xml";
// Note: Update this Rule name to a different deprecated Rule when the one listed here is finally removed.
private static final String DEPRECATED_RULE_NAME = "EmptyCatchBlock";
private static final String REFERENCE_TO_DEPRECATED_RULE = "<?xml version=\"1.0\"?>" + PMD.EOL
+ "<ruleset name=\"test\">" + PMD.EOL + "<description>testdesc</description>" + PMD.EOL + "<rule "
+ PMD.EOL + "ref=\"" + DEPRECATED_RULE_RULESET_NAME + "/" + DEPRECATED_RULE_NAME + "\">" + PMD.EOL
+ "</rule></ruleset>";
private static final String REFERENCE_TO_RULESET_WITH_DEPRECATED_RULE = "<?xml version=\"1.0\"?>" + PMD.EOL
+ "<ruleset name=\"test\">" + PMD.EOL + "<description>testdesc</description>" + PMD.EOL + "<rule "
+ PMD.EOL + "ref=\"" + DEPRECATED_RULE_RULESET_NAME + "\">" + PMD.EOL + "</rule></ruleset>";
private static final String DFA = "<?xml version=\"1.0\"?>" + PMD.EOL + "<ruleset name=\"test\">" + PMD.EOL
+ "<description>testdesc</description>" + PMD.EOL + "<rule " + PMD.EOL + "name=\"MockRuleName\" " + PMD.EOL
+ "message=\"avoid the mock rule\" " + PMD.EOL + "dfa=\"true\" " + PMD.EOL
@@ -787,6 +846,10 @@ public class RuleSetFactoryTest {
+ "<exclude-pattern>exclude2</exclude-pattern>" + PMD.EOL + "<exclude-pattern>exclude3</exclude-pattern>"
+ PMD.EOL + "</ruleset>";
private static final String EXTERNAL_REFERENCE_RULE_SET = "<?xml version=\"1.0\"?>" + PMD.EOL
+ "<ruleset name=\"test\">" + PMD.EOL + "<description>testdesc</description>" + PMD.EOL
+ "<rule ref=\"rulesets/unusedcode.xml/UnusedLocalVariable\"/>" + PMD.EOL + "</ruleset>";
private Rule loadFirstRule(String ruleSetXml) {
RuleSet rs = loadRuleSet(ruleSetXml);
return rs.getRules().iterator().next();
@@ -809,10 +872,6 @@ public class RuleSetFactoryTest {
assertEquals(UnusedLocalVariableRule.class.getName(), rs.getRuleByName("UnusedLocalVariable").getRuleClass());
}
private static final String EXTERNAL_REFERENCE_RULE_SET = "<?xml version=\"1.0\"?>" + PMD.EOL
+ "<ruleset name=\"test\">" + PMD.EOL + "<description>testdesc</description>" + PMD.EOL
+ "<rule ref=\"rulesets/unusedcode.xml/UnusedLocalVariable\"/>" + PMD.EOL + "</ruleset>";
public static junit.framework.Test suite() {
return new JUnit4TestAdapter(RuleSetFactoryTest.class);
}
+21 -3
View File
@@ -762,7 +762,25 @@ public class Foo {
]]>
</example>
</rule>
<rule ref="rulesets/empty.xml"/>
<rule ref="rulesets/useless.xml"/>
<rule deprecated="true" ref="rulesets/empty.xml/EmptyCatchBlock" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptyIfStmt" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptyWhileStmt" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptyTryBlock" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptyFinallyBlock" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptySwitchStatements" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptySynchronizedBlock" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptyStatementNotInLoop" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptyInitializer" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptyStatementBlock" />
<rule deprecated="true" ref="rulesets/empty.xml/EmptyStaticInitializer" />
<rule deprecated="true" ref="rulesets/useless.xml/UnnecessaryConversionTemporary" />
<rule deprecated="true" ref="rulesets/useless.xml/UnnecessaryReturn" />
<rule deprecated="true" ref="rulesets/useless.xml/UnnecessaryFinalModifier" />
<rule deprecated="true" ref="rulesets/useless.xml/UselessOverridingMethod" />
<rule deprecated="true" ref="rulesets/useless.xml/UselessOperationOnImmutable" />
<rule deprecated="true" ref="rulesets/useless.xml/UnusedNullCheckInEquals" />
<rule deprecated="true" ref="rulesets/useless.xml/UselessParentheses" />
</ruleset>
+14 -29
View File
@@ -17,69 +17,54 @@ Rules to check PMD itself
<exclude-pattern>.*cppast/.*.java</exclude-pattern>
<rule ref="rulesets/basic.xml/AvoidMultipleUnaryOperators"/>
<rule ref="rulesets/basic.xml/UselessOverridingMethod"/>
<rule ref="rulesets/basic.xml/BooleanInstantiation"/>
<rule ref="rulesets/basic.xml/EmptyStatementNotInLoop"/>
<rule ref="rulesets/basic.xml/OverrideBothEqualsAndHashcode"/>
<rule ref="rulesets/design.xml/OptimizableToArrayCall"/>
<rule ref="rulesets/design.xml/IdempotentOperations"/>
<rule ref="rulesets/design.xml/SingularField"/>
<rule ref="rulesets/imports.xml"/>
<rule ref="rulesets/strictexception.xml/AvoidRethrowingException"/>
<rule ref="rulesets/strictexception.xml/AvoidCatchingThrowable"/>
<rule ref="rulesets/optimizations.xml/UnnecessaryWrapperObjectCreation"/>
<rule ref="rulesets/optimizations.xml/AvoidArrayLoops"/>
<rule ref="rulesets/strings.xml/UnnecessaryCaseChange"/>
<rule ref="rulesets/strings.xml/UseEqualsToCompareStrings"/>
<rule ref="rulesets/strings.xml/UselessStringValueOf"/>
<rule ref="rulesets/strings.xml/AppendCharacterWithChar"/>
<rule ref="rulesets/controversial.xml/UnnecessaryParentheses"/>
<!-- Null checks -->
<rule ref="rulesets/basic.xml/BrokenNullCheck"/>
<rule ref="rulesets/basic.xml/MisplacedNullCheck"/>
<rule ref="rulesets/basic.xml/MisplacedNullCheck"/>
<rule ref="rulesets/design.xml/EqualsNull"/>
<!-- Null checks End -->
<!-- Empty thingies -->
<rule ref="rulesets/basic.xml/EmptyWhileStmt"/>
<rule ref="rulesets/basic.xml/EmptyTryBlock"/>
<rule ref="rulesets/basic.xml/EmptyFinallyBlock"/>
<rule ref="rulesets/basic.xml/EmptySwitchStatements"/>
<rule ref="rulesets/basic.xml/EmptySynchronizedBlock"/>
<rule ref="rulesets/basic.xml/EmptyStaticInitializer"/>
<rule ref="rulesets/empty.xml">
<exclude name="EmptyCatchBlock" />
</rule>
<rule ref="rulesets/empty.xml/EmptyCatchBlock">
<properties>
<property name="allowCommentedBlocks" value="true" />
</properties>
</rule>
<!-- Empty thingies End -->
<!-- Unused code-->
<rule ref="rulesets/unusedcode.xml/UnusedPrivateField"/>
<rule ref="rulesets/unusedcode.xml/UnusedLocalVariable"/>
<!-- Unused code End-->
<!-- Unnecessary code -->
<rule ref="rulesets/useless.xml/UselessOverridingMethod"/>
<!-- Unnecessary code End -->
<!-- Specifically reference via other referencing RuleSets to test Reference to Reference -->
<rule ref="rulesets/migrating_to_13.xml"/>
<rule ref="rulesets/migrating_to_14.xml"/>
+2
View File
@@ -259,7 +259,9 @@ public class PMD {
RuleSetFactory ruleSetFactory = new RuleSetFactory();
ruleSetFactory.setMinimumPriority(opts.getMinPriority());
ruleSetFactory.setWarnDeprecated(true);
RuleSets rulesets = ruleSetFactory.createRuleSets(opts.getRulesets());
ruleSetFactory.setWarnDeprecated(false);
printRuleNamesInDebug(rulesets);
long endLoadRules = System.nanoTime();
Benchmark.mark(Benchmark.TYPE_LOAD_RULES, endLoadRules - startLoadRules, 0);
+17
View File
@@ -58,6 +58,23 @@ public interface Rule {
*/
void setMaximumLanguageVersion(LanguageVersion maximumLanguageVersion);
/**
* Gets whether this Rule is deprecated. A deprecated Rule is one which:
* <ul>
* <li>is scheduled for removal in a future version of PMD</li>
* <li>or, has been removed and replaced with a non-functioning place-holder
* and will be completely removed in a future version of PMD</li>
* <li>or, has been renamed/moved and the old name will be completely
* removed in a future version of PMD</li>
* <ul>
*/
boolean isDeprecated();
/**
* Sets whether this Rule is deprecated.
*/
void setDeprecated(boolean deprecated);
/**
* Get the name of this Rule.
*/
+96 -19
View File
@@ -5,10 +5,12 @@ package net.sourceforge.pmd;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -16,6 +18,7 @@ import javax.xml.parsers.ParserConfigurationException;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.rule.MockRule;
import net.sourceforge.pmd.lang.rule.RuleReference;
import net.sourceforge.pmd.util.ResourceLoader;
@@ -30,7 +33,29 @@ import org.xml.sax.SAXException;
*/
public class RuleSetFactory {
private static final Logger LOG = Logger.getLogger(RuleSetFactory.class.getName());
private RulePriority minPriority = RulePriority.LOW;
private boolean warnDeprecated = false;
// This is a cache of RuleSets loaded while loading RuleSets in bulk. It is not used during individual RuleSet loading.
private Map<String, RuleSet> ruleSetCache = new HashMap<String, RuleSet>();
/**
* Default constructor.
*/
public RuleSetFactory() {
}
/**
* This constructor is to be used internally when there is a need to load
* a RuleSet using default settings. Certain global state will be
* propagated between the original RuleSetFactory and the new RuleSetFactory.
* @param ruleSetFactory The RuleSetFactory creating the new RuleSetFactory.
*/
private RuleSetFactory(RuleSetFactory ruleSetFactory) {
this.ruleSetCache = ruleSetFactory.ruleSetCache;
}
/**
* Set the minimum rule priority threshold for all Rules which are loaded
@@ -42,6 +67,14 @@ public class RuleSetFactory {
this.minPriority = minPriority;
}
/**
* Set whether warning messages should be logged for usage of deprecated Rules.
* @param warnDeprecated <code>true</code> to log warning messages.
*/
public void setWarnDeprecated(boolean warnDeprecated) {
this.warnDeprecated = warnDeprecated;
}
/**
* Returns an Iterator of RuleSet objects loaded from descriptions from the
* "rulesets.properties" resource.
@@ -70,6 +103,7 @@ public class RuleSetFactory {
* @throws RuleSetNotFoundException if unable to find a resource.
*/
public RuleSets createRuleSets(String ruleSetFileNames) throws RuleSetNotFoundException {
// Warning: This method should not be used to implement the internals of RuleSetFactory, because it does not take an explicit ClassLoader.
return createRuleSets(ruleSetFileNames, getClass().getClassLoader());
}
@@ -82,14 +116,21 @@ public class RuleSetFactory {
* @throws RuleSetNotFoundException if unable to find a resource.
*/
public RuleSets createRuleSets(String ruleSetFileNames, ClassLoader classLoader) throws RuleSetNotFoundException {
RuleSets ruleSets = new RuleSets();
try {
// Create the cache for bulk RuleSet processing.
ruleSetCache = new HashMap<String, RuleSet>();
RuleSets ruleSets = new RuleSets();
for (StringTokenizer st = new StringTokenizer(ruleSetFileNames, ","); st.hasMoreTokens();) {
RuleSet ruleSet = createSingleRuleSet(st.nextToken().trim(), classLoader);
ruleSets.addRuleSet(ruleSet);
for (StringTokenizer st = new StringTokenizer(ruleSetFileNames, ","); st.hasMoreTokens();) {
RuleSet ruleSet = createSingleRuleSet(st.nextToken().trim(), classLoader);
ruleSets.addRuleSet(ruleSet);
}
return ruleSets;
} finally {
// Remove the cache, so we don't affect behavior of subsequent or single RuleSet processing.
ruleSetCache = null;
}
return ruleSets;
}
/**
@@ -122,6 +163,7 @@ public class RuleSetFactory {
* @throws RuleSetNotFoundException if unable to find a resource.
*/
public RuleSet createSingleRuleSet(String ruleSetFileName) throws RuleSetNotFoundException {
// Warning: This method should not be used to implement the internals of RuleSetFactory, because it does not take an explicit ClassLoader.
return createSingleRuleSet(ruleSetFileName, getClass().getClassLoader());
}
@@ -135,7 +177,18 @@ public class RuleSetFactory {
*/
private RuleSet createSingleRuleSet(String ruleSetFileName, ClassLoader classLoader)
throws RuleSetNotFoundException {
return parseRuleSetNode(ruleSetFileName, tryToGetStreamTo(ruleSetFileName, classLoader), classLoader);
// If we have a RuleSet cache, check in there first.
RuleSet ruleSet = null;
if (ruleSetCache != null) {
ruleSet = ruleSetCache.get(ruleSetFileName);
}
if (ruleSet == null) {
ruleSet = parseRuleSetNode(ruleSetFileName, tryToGetStreamTo(ruleSetFileName, classLoader), classLoader);
if (ruleSetCache != null) {
ruleSetCache.put(ruleSet.getFileName(), ruleSet);
}
}
return ruleSet;
}
/**
@@ -146,6 +199,7 @@ public class RuleSetFactory {
* @return A new RuleSet.
*/
public RuleSet createRuleSet(InputStream inputStream) {
// Warning: This method should not be used to implement the internals of RuleSetFactory, because it does not take an explicit ClassLoader.
return createRuleSet(inputStream, getClass().getClassLoader());
}
@@ -251,24 +305,26 @@ public class RuleSetFactory {
Element ruleElement = (Element) ruleNode;
String ref = ruleElement.getAttribute("ref");
if (ref.endsWith("xml")) {
parseRuleSetReferenceNode(ruleSet, ruleElement, ref);
parseRuleSetReferenceNode(ruleSet, ruleElement, ref, classLoader);
} else if (ref.trim().length() == 0) {
parseSingleRuleNode(ruleSet, ruleNode, classLoader);
} else {
parseRuleReferenceNode(ruleSet, ruleNode, ref);
parseRuleReferenceNode(ruleSet, ruleNode, ref, classLoader);
}
}
/**
* Parse a rule node as an RuleSetReference for all Rules. Every Rule from
* the referred to RuleSet will be added as a RuleReference except for those
* explicitly excluded.
* explicitly excluded, below the minimum priority threshold for this
* RuleSetFactory, or which are deprecated.
*
* @param ruleSet The RuleSet being constructed.
* @param ruleElement Must be a rule element node.
* @param ref The RuleSet reference.
* @param classLoader The ClassLoader to load Classes and resources.
*/
private void parseRuleSetReferenceNode(RuleSet ruleSet, Element ruleElement, String ref)
private void parseRuleSetReferenceNode(RuleSet ruleSet, Element ruleElement, String ref, ClassLoader classLoader)
throws RuleSetNotFoundException {
RuleSetReference ruleSetReference = new RuleSetReference();
@@ -283,11 +339,12 @@ public class RuleSetFactory {
}
}
RuleSetFactory ruleSetFactory = new RuleSetFactory();
RuleSet otherRuleSet = ruleSetFactory.createRuleSet(ResourceLoader.loadResourceAsStream(ref));
RuleSetFactory ruleSetFactory = new RuleSetFactory(this);
RuleSet otherRuleSet = ruleSetFactory.createSingleRuleSet(ref, classLoader);
for (Rule rule : otherRuleSet.getRules()) {
if (!ruleSetReference.getExcludes().contains(rule.getName())
&& rule.getPriority().compareTo(minPriority) <= 0) {
&& rule.getPriority().compareTo(minPriority) <= 0
&& !rule.isDeprecated()) {
RuleReference ruleReference = new RuleReference();
ruleReference.setRuleSetReference(ruleSetReference);
ruleReference.setRule(rule);
@@ -409,21 +466,38 @@ public class RuleSetFactory {
*
* @param ruleSet The RuleSet being constructed.
* @param ruleNode Must be a rule element node.
* @param classLoader The ClassLoader to load Classes and resources.
* @param ref A reference to a Rule.
* @param classLoader The ClassLoader to load Classes and resources.
*/
private void parseRuleReferenceNode(RuleSet ruleSet, Node ruleNode, String ref) throws RuleSetNotFoundException {
RuleSetFactory ruleSetFactory = new RuleSetFactory();
private void parseRuleReferenceNode(RuleSet ruleSet, Node ruleNode, String ref, ClassLoader classLoader)
throws RuleSetNotFoundException {
RuleSetFactory ruleSetFactory = new RuleSetFactory(this);
ExternalRuleID externalRuleID = new ExternalRuleID(ref);
RuleSet externalRuleSet = ruleSetFactory.createRuleSet(ResourceLoader.loadResourceAsStream(externalRuleID
.getFilename()));
RuleSet externalRuleSet = ruleSetFactory.createSingleRuleSet(externalRuleID.getFilename(), classLoader);
Rule externalRule = externalRuleSet.getRuleByName(externalRuleID.getRuleName());
if (externalRule == null) {
throw new IllegalArgumentException("Unable to find rule " + externalRuleID.getRuleName()
+ "; perhaps the rule name is mispelled?");
}
if (warnDeprecated && externalRule.isDeprecated()) {
if (externalRule instanceof RuleReference) {
RuleReference ruleReference = (RuleReference) externalRule;
LOG.warning("Use Rule name " + ruleReference.getRuleSetReference().getRuleSetFileName() + "/"
+ ruleReference.getName() + " instead of the deprecated Rule name " + externalRuleID
+ ". Future versions of PMD will remove support for this deprecated Rule name usage.");
} else if (externalRule instanceof MockRule) {
LOG.warning("Discontinue using Rule name " + externalRuleID
+ " as it has been removed from PMD and no longer functions."
+ " Future versions of PMD will remove support for this Rule.");
} else {
LOG.warning("Discontinue using Rule name " + externalRuleID
+ " as it is scheduled for removal from PMD."
+ " Future versions of PMD will remove support for this Rule.");
}
}
RuleSetReference ruleSetReference = new RuleSetReference();
ruleSetReference.setAllRules(false);
ruleSetReference.setRuleSetFileName(externalRuleID.getFilename());
@@ -433,6 +507,9 @@ public class RuleSetFactory {
ruleReference.setRule(externalRule);
Element ruleElement = (Element) ruleNode;
if (ruleElement.hasAttribute("deprecated")) {
ruleReference.setDeprecated(Boolean.parseBoolean(ruleElement.getAttribute("deprecated")));
}
if (ruleElement.hasAttribute("name")) {
ruleReference.setName(ruleElement.getAttribute("name"));
}
+12 -8
View File
@@ -138,6 +138,7 @@ public class RuleSetWriter {
Language language = ruleReference.getOverriddenLanguage();
LanguageVersion minimumLanguageVersion = ruleReference.getOverriddenMinimumLanguageVersion();
LanguageVersion maximumLanguageVersion = ruleReference.getOverriddenMaximumLanguageVersion();
Boolean deprecated = ruleReference.isOverriddenDeprecated();
String name = ruleReference.getOverriddenName();
String ref = ruleReference.getRuleSetReference().getRuleSetFileName() + "/" + ruleReference.getName();
String message = ruleReference.getOverriddenMessage();
@@ -146,22 +147,22 @@ public class RuleSetWriter {
RulePriority priority = ruleReference.getOverriddenPriority();
Properties properties = ruleReference.getOverriddenProperties();
List<String> examples = ruleReference.getOverriddenExamples();
return createSingleRuleElement(language, minimumLanguageVersion, maximumLanguageVersion, name, null,
return createSingleRuleElement(language, minimumLanguageVersion, maximumLanguageVersion, deprecated, name, null,
ref, message, externalInfoUrl, null, null, null, description, priority, properties, examples);
}
} else {
return createSingleRuleElement(rule instanceof ImmutableLanguage ? null : rule.getLanguage(), rule
.getMinimumLanguageVersion(), rule.getMaximumLanguageVersion(), rule.getName(), rule.getSince(),
null, rule.getMessage(), rule.getExternalInfoUrl(), rule.getRuleClass(), rule.usesDFA(), rule
.usesTypeResolution(), rule.getDescription(), rule.getPriority(), rule.getProperties(),
rule.getExamples());
.getMinimumLanguageVersion(), rule.getMaximumLanguageVersion(), rule.isDeprecated(),
rule.getName(), rule.getSince(), null, rule.getMessage(), rule.getExternalInfoUrl(), rule
.getRuleClass(), rule.usesDFA(), rule.usesTypeResolution(), rule.getDescription(), rule
.getPriority(), rule.getProperties(), rule.getExamples());
}
}
private Element createSingleRuleElement(Language language, LanguageVersion minimumLanguageVersion,
LanguageVersion maximumLanguageVersion, String name, String since, String ref, String message,
String externalInfoUrl, String clazz, Boolean dfa, Boolean typeResolution, String description,
RulePriority priority, Properties properties, List<String> examples) {
LanguageVersion maximumLanguageVersion, Boolean deprecated, String name, String since, String ref,
String message, String externalInfoUrl, String clazz, Boolean dfa, Boolean typeResolution,
String description, RulePriority priority, Properties properties, List<String> examples) {
Element ruleElement = document.createElement("rule");
if (language != null) {
ruleElement.setAttribute("language", language.getTerseName());
@@ -172,6 +173,9 @@ public class RuleSetWriter {
if (maximumLanguageVersion != null) {
ruleElement.setAttribute("maximumLanguageVersion", maximumLanguageVersion.getVersion());
}
if (deprecated != null) {
ruleElement.setAttribute("deprecated", deprecated.toString());
}
if (name != null) {
ruleElement.setAttribute("name", name);
}
@@ -194,7 +194,9 @@ public class PMDTask extends Task {
// This is just used to validate and display rules. Each thread will create its own ruleset
RuleSets rules;
ruleSetFactory.setMinimumPriority(minPriority);
ruleSetFactory.setWarnDeprecated(true);
rules = ruleSetFactory.createRuleSets(ruleSetFiles, classLoader);
ruleSetFactory.setWarnDeprecated(false);
logRulesUsed(rules);
} catch (RuleSetNotFoundException e) {
throw new BuildException(e.getMessage());
@@ -17,7 +17,7 @@ import net.sourceforge.pmd.lang.ast.Node;
/**
* Base class for Rule implementations which delegate to another Rule instance.
*/
public class AbstractDelegateRule implements Rule {
public abstract class AbstractDelegateRule implements Rule {
private Rule rule;
public AbstractDelegateRule() {
@@ -55,6 +55,14 @@ public class AbstractDelegateRule implements Rule {
return rule.getMaximumLanguageVersion();
}
public boolean isDeprecated() {
return rule.isDeprecated();
}
public void setDeprecated(boolean deprecated) {
rule.setDeprecated(deprecated);
}
public String getName() {
return rule.getName();
}
@@ -34,6 +34,7 @@ public abstract class AbstractRule implements Rule {
private Language language;
private LanguageVersion minimumLanguageVersion;
private LanguageVersion maximumLanguageVersion;
private boolean deprecated;
private String name = getClass().getName();
private String since;
private String ruleClass = getClass().getName();
@@ -76,6 +77,14 @@ public abstract class AbstractRule implements Rule {
this.maximumLanguageVersion = maximumLanguageVersion;
}
public boolean isDeprecated() {
return deprecated;
}
public void setDeprecated(boolean deprecated) {
this.deprecated = deprecated;
}
public String getName() {
return name;
}
@@ -23,6 +23,7 @@ public class RuleReference extends AbstractDelegateRule {
private Language language;
private LanguageVersion minimumLanguageVersion;
private LanguageVersion maximumLanguageVersion;
private Boolean deprecated;
private String name;
private Properties properties;
private String message;
@@ -71,6 +72,22 @@ public class RuleReference extends AbstractDelegateRule {
}
}
public Boolean isOverriddenDeprecated() {
return deprecated;
}
@Override
public boolean isDeprecated() {
return deprecated != null && deprecated.booleanValue();
}
@Override
public void setDeprecated(boolean deprecated) {
// Deprecation does not propagate to the underlying Rule. It is the
// Rule reference itself which is being deprecated.
this.deprecated = deprecated ? deprecated : null;
}
public String getOverriddenName() {
return name;
}
@@ -199,8 +216,8 @@ public class RuleReference extends AbstractDelegateRule {
return StringUtil.isSame(s1, s2, true, false, true);
}
private static boolean isSame(Enum<?> e1, Enum<?> e2) {
return e1 == e2 || (e1 != null && e2 != null && e1.equals(e2));
private static boolean isSame(Object o1, Object o2) {
return o1 == o2 || (o1 != null && o2 != null && o1.equals(o2));
}
private static boolean contains(Collection<String> collection, String s1) {