diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
index 2925ce0dc6..b3a54e706b 100644
--- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
+++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java
@@ -4,6 +4,7 @@
package net.sourceforge.pmd.docs;
+import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
@@ -42,6 +43,8 @@ public class RuleDocGenerator {
private static final String DEPRECATION_LABEL_SMALL = "Deprecated ";
private static final String DEPRECATION_LABEL = "Deprecated ";
+ private static final String GITHUB_SOURCE_LINK = "https://github.com/pmd/pmd/blob/master/";
+
private final Path root;
private final FileWriter writer;
@@ -277,7 +280,7 @@ public class RuleDocGenerator {
lines.add("");
}
if (rule.getSince() != null) {
- lines.add("**Since:** " + rule.getSince());
+ lines.add("**Since:** PMD " + rule.getSince());
lines.add("");
}
lines.add("**Priority:** " + rule.getPriority() + " (" + rule.getPriority().getPriority() + ")");
@@ -285,6 +288,20 @@ public class RuleDocGenerator {
lines.add(StringUtils.stripToEmpty(rule.getDescription()));
lines.add("");
+
+ if (rule instanceof XPathRule || rule instanceof RuleReference && ((RuleReference) rule).getRule() instanceof XPathRule) {
+ lines.add("```");
+ lines.add(StringUtils.stripToEmpty(rule.getProperty(XPathRule.XPATH_DESCRIPTOR)));
+ lines.add("```");
+ lines.add("");
+ } else {
+ lines.add("**This rule is defined by the following Java class:** "
+ + "[" + rule.getRuleClass() + "]("
+ + GITHUB_SOURCE_LINK + getRuleClassSourceFilepath(rule.getRuleClass())
+ + ")");
+ lines.add("");
+ }
+
if (!rule.getExamples().isEmpty()) {
lines.add("**Example(s):**");
lines.add("");
@@ -358,7 +375,7 @@ public class RuleDocGenerator {
return super.visitFile(file, attrs);
}
});
-
+
if (!foundPathResult.isEmpty()) {
Path foundPath = foundPathResult.get(0);
foundPath = root.relativize(foundPath);
@@ -367,4 +384,29 @@ public class RuleDocGenerator {
return StringUtils.chomp(ruleset.getFileName());
}
+
+ private String getRuleClassSourceFilepath(String ruleClass) throws IOException {
+ final String relativeSourceFilename = ruleClass.replaceAll("\\.", File.separator) + ".java";
+ final List foundPathResult = new LinkedList<>();
+
+ Files.walkFileTree(root, new SimpleFileVisitor() {
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+ String path = file.toString();
+ if (path.contains("src") && path.endsWith(relativeSourceFilename)) {
+ foundPathResult.add(file);
+ return FileVisitResult.TERMINATE;
+ }
+ return super.visitFile(file, attrs);
+ }
+ });
+
+ if (!foundPathResult.isEmpty()) {
+ Path foundPath = foundPathResult.get(0);
+ foundPath = root.relativize(foundPath);
+ return FilenameUtils.normalize(foundPath.toString(), true);
+ }
+
+ return FilenameUtils.normalize(relativeSourceFilename, true);
+ }
}
diff --git a/pmd-doc/src/test/resources/expected/sample.md b/pmd-doc/src/test/resources/expected/sample.md
index bc50e5667d..2ff1e05272 100644
--- a/pmd-doc/src/test/resources/expected/sample.md
+++ b/pmd-doc/src/test/resources/expected/sample.md
@@ -10,20 +10,33 @@ editmepath: ../rulesets/ruledoctest/sample.xml
Deprecated
-**Since:** 1.0
+**Since:** PMD 1.0
**Priority:** Medium (3)
Just some description of a deprecated rule.
+```
+//ForStatement
+```
+
## JumbledIncrementer
-**Since:** 1.0
+**Since:** PMD 1.0
**Priority:** Medium (3)
Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional.
+```
+//ForStatement
+ [
+ ForUpdate/StatementExpressionList/StatementExpression/PostfixExpression/PrimaryExpression/PrimaryPrefix/Name/@Image
+ =
+ ancestor::ForStatement/ForInit//VariableDeclaratorId/@Image
+ ]
+```
+
**Example(s):**
```
@@ -44,12 +57,21 @@ public class JumbledIncrementerRule1 {
The rule has been moved to another ruleset. Use instead: [JumbledIncrementer](pmd_rules_java_basic.html#jumbledincrementer)
-**Since:** 1.0
+**Since:** PMD 1.0
**Priority:** Medium (3)
Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional.
+```
+//ForStatement
+ [
+ ForUpdate/StatementExpressionList/StatementExpression/PostfixExpression/PrimaryExpression/PrimaryPrefix/Name/@Image
+ =
+ ancestor::ForStatement/ForInit//VariableDeclaratorId/@Image
+ ]
+```
+
**Example(s):**
```
@@ -66,12 +88,14 @@ public class JumbledIncrementerRule1 {
## OverrideBothEqualsAndHashcode
-**Since:** 0.4
+**Since:** PMD 0.4
**Priority:** Medium (3)
Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass.
+**This rule is defined by the following Java class:** [net.sourceforge.pmd.lang.java.rule.basic.OverrideBothEqualsAndHashcodeRule](https://github.com/pmd/pmd/blob/master/net/sourceforge/pmd/lang/java/rule/basic/OverrideBothEqualsAndHashcodeRule.java)
+
**Example(s):**
```
@@ -103,12 +127,21 @@ public class Foo { // perfect, both methods provided
This rule has been renamed. Use instead: [JumbledIncrementer](#jumbledincrementer)
-**Since:** 1.0
+**Since:** PMD 1.0
**Priority:** Medium (3)
Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional.
+```
+//ForStatement
+ [
+ ForUpdate/StatementExpressionList/StatementExpression/PostfixExpression/PrimaryExpression/PrimaryPrefix/Name/@Image
+ =
+ ancestor::ForStatement/ForInit//VariableDeclaratorId/@Image
+ ]
+```
+
**Example(s):**
```