[doc] Renamed/Moved rules are missing in documentation (Fixes #2823)
Include deprecated rule references conditionally when loading rulesets via RuleSetFactory
This commit is contained in:
@ -25,6 +25,8 @@ This is a {{ site.pmd.release_type }} release.
|
||||
|
||||
* java-errorprone
|
||||
* [#2157](https://github.com/pmd/pmd/issues/2157): \[java] Improve DoNotCallSystemExit: permit call in main(), flag System.halt
|
||||
* miscellaneous
|
||||
* [#2823](https://github.com/pmd/pmd/issues/2823): \[doc] Renamed/Moved rules are missing in documentation
|
||||
|
||||
### API Changes
|
||||
|
||||
|
@ -45,9 +45,10 @@ import net.sourceforge.pmd.util.ResourceLoader;
|
||||
/**
|
||||
* RuleSetFactory is responsible for creating RuleSet instances from XML
|
||||
* content. By default Rules will be loaded using the {@link RulePriority#LOW} priority,
|
||||
* with Rule deprecation warnings off.
|
||||
* By default, the ruleset compatibility filter is active, too.
|
||||
* See {@link RuleSetFactoryCompatibility}.
|
||||
* with Rule deprecation warnings off;
|
||||
* the ruleset compatibility filter is active, too (see {@link RuleSetFactoryCompatibility});
|
||||
* if the ruleset contains rule references (e.g. for renamed or moved rules), these
|
||||
* are ignored by default.
|
||||
*/
|
||||
public class RuleSetFactory {
|
||||
|
||||
@ -61,6 +62,7 @@ public class RuleSetFactory {
|
||||
private final RulePriority minimumPriority;
|
||||
private final boolean warnDeprecated;
|
||||
private final RuleSetFactoryCompatibility compatibilityFilter;
|
||||
private final boolean includeDeprecatedRuleReferences;
|
||||
|
||||
private final Map<RuleSetReferenceId, RuleSet> parsedRulesets = new HashMap<>();
|
||||
|
||||
@ -89,9 +91,15 @@ public class RuleSetFactory {
|
||||
@Deprecated // to be hidden with PMD 7.0.0.
|
||||
public RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority,
|
||||
final boolean warnDeprecated, final boolean enableCompatibility) {
|
||||
this(resourceLoader, minimumPriority, warnDeprecated, enableCompatibility, false);
|
||||
}
|
||||
|
||||
RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority,
|
||||
final boolean warnDeprecated, final boolean enableCompatibility, boolean includeDeprecatedRuleReferences) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
this.minimumPriority = minimumPriority;
|
||||
this.warnDeprecated = warnDeprecated;
|
||||
this.includeDeprecatedRuleReferences = includeDeprecatedRuleReferences;
|
||||
|
||||
if (enableCompatibility) {
|
||||
this.compatibilityFilter = new RuleSetFactoryCompatibility();
|
||||
@ -133,27 +141,25 @@ public class RuleSetFactory {
|
||||
*/
|
||||
public Iterator<RuleSet> getRegisteredRuleSets() throws RuleSetNotFoundException {
|
||||
String rulesetsProperties = null;
|
||||
try {
|
||||
List<RuleSetReferenceId> ruleSetReferenceIds = new ArrayList<>();
|
||||
for (Language language : LanguageRegistry.findWithRuleSupport()) {
|
||||
Properties props = new Properties();
|
||||
rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties";
|
||||
try (InputStream inputStream = resourceLoader.loadClassPathResourceAsStreamOrThrow(rulesetsProperties)) {
|
||||
props.load(inputStream);
|
||||
String rulesetFilenames = props.getProperty("rulesets.filenames");
|
||||
if (rulesetFilenames != null) {
|
||||
ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames));
|
||||
}
|
||||
} catch (RuleSetNotFoundException e) {
|
||||
LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + ".");
|
||||
List<RuleSetReferenceId> ruleSetReferenceIds = new ArrayList<>();
|
||||
for (Language language : LanguageRegistry.findWithRuleSupport()) {
|
||||
Properties props = new Properties();
|
||||
rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties";
|
||||
try (InputStream inputStream = resourceLoader.loadClassPathResourceAsStreamOrThrow(rulesetsProperties)) {
|
||||
props.load(inputStream);
|
||||
String rulesetFilenames = props.getProperty("rulesets.filenames");
|
||||
if (rulesetFilenames != null) {
|
||||
ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames));
|
||||
}
|
||||
} catch (RuleSetNotFoundException e) {
|
||||
LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + ".");
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Couldn't find " + rulesetsProperties
|
||||
+ "; please ensure that the directory is on the classpath. The current classpath is: "
|
||||
+ System.getProperty("java.class.path"));
|
||||
}
|
||||
return createRuleSets(ruleSetReferenceIds).getRuleSetsIterator();
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("Couldn't find " + rulesetsProperties
|
||||
+ "; please ensure that the directory is on the classpath. The current classpath is: "
|
||||
+ System.getProperty("java.class.path"));
|
||||
}
|
||||
return createRuleSets(ruleSetReferenceIds).getRuleSetsIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,7 +231,7 @@ public class RuleSetFactory {
|
||||
* if unable to find a resource.
|
||||
*/
|
||||
public RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException {
|
||||
return createRuleSet(ruleSetReferenceId, false);
|
||||
return createRuleSet(ruleSetReferenceId, includeDeprecatedRuleReferences);
|
||||
}
|
||||
|
||||
private RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences)
|
||||
|
@ -174,6 +174,32 @@ public final class RulesetsFactoryUtils {
|
||||
return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ruleset factory which uses the classloader for PMD
|
||||
* classes to resolve resource references.
|
||||
*
|
||||
* @param minimumPriority Minimum priority for rules to be included
|
||||
* @param warnDeprecated If true, print warnings when deprecated rules are included
|
||||
* @param enableCompatibility If true, rule references to moved rules are mapped to their
|
||||
* new location if they are known
|
||||
* @param includeDeprecatedRuleReferences If true, deprecated rule references are retained. Usually, these
|
||||
* references are ignored, since they indicate renamed/moved rules, and the referenced
|
||||
* rule is often included in the same ruleset. Enabling this might result in
|
||||
* duplicated rules.
|
||||
*
|
||||
* @return A ruleset factory
|
||||
*
|
||||
* @see #createFactory(PMDConfiguration)
|
||||
*/
|
||||
public static RuleSetFactory createFactory(RulePriority minimumPriority,
|
||||
boolean warnDeprecated,
|
||||
boolean enableCompatibility,
|
||||
boolean includeDeprecatedRuleReferences) {
|
||||
|
||||
return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility,
|
||||
includeDeprecatedRuleReferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* If in debug modus, print the names of the rules.
|
||||
*
|
||||
|
@ -245,6 +245,30 @@ public class RuleSetFactoryTest {
|
||||
assertTrue(logging.getLog().isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an example of a category (built-in) ruleset, which contains a rule, that has been renamed.
|
||||
* This means: a rule definition for "NewName" and a rule reference "OldName", that is deprecated
|
||||
* and exists for backwards compatibility.
|
||||
*
|
||||
* <p>When loading this ruleset at a whole for generating the documentation, we should still
|
||||
* include the deprecated rule reference, so that we can create a nice documentation.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testRuleSetWithDeprecatedRenamedRuleForDoc() throws Exception {
|
||||
RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true);
|
||||
RuleSet rs = rsf.createRuleSet(createRuleSetReferenceId("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<ruleset name=\"test\">\n"
|
||||
+ " <description>ruleset desc</description>\n"
|
||||
+ " <rule deprecated=\"true\" ref=\"NewName\" name=\"OldName\"/>"
|
||||
+ " <rule name=\"NewName\" message=\"m\" class=\"net.sourceforge.pmd.lang.rule.XPathRule\" language=\"dummy\">"
|
||||
+ " <description>d</description>\n" + " <priority>2</priority>\n" + " </rule>"
|
||||
+ "</ruleset>"));
|
||||
assertEquals(2, rs.getRules().size());
|
||||
assertNotNull(rs.getRuleByName("NewName"));
|
||||
assertNotNull(rs.getRuleByName("OldName"));
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an example of a custom user ruleset, that references a rule, that has been renamed.
|
||||
* The user should get a deprecation warning.
|
||||
|
@ -19,6 +19,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import net.sourceforge.pmd.RulePriority;
|
||||
import net.sourceforge.pmd.RuleSet;
|
||||
import net.sourceforge.pmd.RuleSetFactory;
|
||||
import net.sourceforge.pmd.RuleSetNotFoundException;
|
||||
@ -39,7 +40,8 @@ public final class GenerateRuleDocsCmd {
|
||||
Path output = FileSystems.getDefault().getPath(args[0]).resolve("..").toAbsolutePath().normalize();
|
||||
System.out.println("Generating docs into " + output);
|
||||
|
||||
RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory();
|
||||
// important: use a RuleSetFactory that includes all rules, e.g. deprecated rule references
|
||||
RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true);
|
||||
Iterator<RuleSet> registeredRuleSets = ruleSetFactory.getRegisteredRuleSets();
|
||||
List<String> additionalRulesets = findAdditionalRulesets(output);
|
||||
|
||||
|
@ -21,6 +21,7 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import net.sourceforge.pmd.RulePriority;
|
||||
import net.sourceforge.pmd.RuleSet;
|
||||
import net.sourceforge.pmd.RuleSetFactory;
|
||||
import net.sourceforge.pmd.RuleSetNotFoundException;
|
||||
@ -61,7 +62,7 @@ public class RuleDocGeneratorTest {
|
||||
public void testSingleRuleset() throws RuleSetNotFoundException, IOException {
|
||||
RuleDocGenerator generator = new RuleDocGenerator(writer, root);
|
||||
|
||||
RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory();
|
||||
RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true);
|
||||
RuleSet ruleset = rsf.createRuleSet("rulesets/ruledoctest/sample.xml");
|
||||
|
||||
generator.generate(Arrays.asList(ruleset).iterator(),
|
||||
|
Reference in New Issue
Block a user