[doc] Fix rule tags in the rule docs
If using quotes, there was a html escape done, which made the rule tag renderer to spit out "quot".
This commit is contained in:
pmd-doc/src
main/java/net/sourceforge/pmd/docs
test
java/net/sourceforge/pmd/docs
resources
pmd-java/src/main/resources/category/java
@ -5,6 +5,8 @@
|
||||
package net.sourceforge.pmd.docs;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.text.StringEscapeUtils;
|
||||
|
||||
@ -12,6 +14,7 @@ public final class EscapeUtils {
|
||||
private static final String BACKTICK = "`";
|
||||
private static final String URL_START = "<http";
|
||||
private static final String QUOTE_START = ">";
|
||||
private static final Pattern RULE_TAG = Pattern.compile("\\{%\\s+rule\\s+"([^&]+)"\\s*\\%}");
|
||||
|
||||
private EscapeUtils() {
|
||||
// This is a utility class
|
||||
@ -83,9 +86,22 @@ public final class EscapeUtils {
|
||||
}
|
||||
if (needsEscape && !line.startsWith(" ")) {
|
||||
line = escapeSingleLine(line);
|
||||
line = preserveRuleTagQuotes(line);
|
||||
}
|
||||
lines.set(i, line);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* If quotes are used for rule tags, e.g. {@code {% rule "OtherRule" %}}, these
|
||||
* quotes might have been escaped for html, but it's actually markdown/jekyll/liquid
|
||||
* and not html. This undoes the escaping.
|
||||
* @param text the already escaped text that might contain rule tags
|
||||
* @return text with the fixed rule tags
|
||||
*/
|
||||
public static String preserveRuleTagQuotes(String text) {
|
||||
Matcher m = RULE_TAG.matcher(text);
|
||||
return m.replaceAll("{% rule \"$1\" %}");
|
||||
}
|
||||
}
|
||||
|
@ -299,15 +299,16 @@ public class RuleDocGenerator {
|
||||
* @return
|
||||
*/
|
||||
private static String getShortRuleDescription(Rule rule) {
|
||||
return StringEscapeUtils.escapeHtml4(
|
||||
String htmlEscaped = StringEscapeUtils.escapeHtml4(
|
||||
StringUtils.abbreviate(
|
||||
StringUtils.stripToEmpty(
|
||||
rule.getDescription()
|
||||
.replaceAll("\n|\r", "")
|
||||
.replaceAll("\n+|\r+", " ")
|
||||
.replaceAll("\\|", "\\\\|")
|
||||
.replaceAll("`", "'")
|
||||
.replaceAll("\\*", "")),
|
||||
100));
|
||||
return EscapeUtils.preserveRuleTagQuotes(htmlEscaped);
|
||||
}
|
||||
|
||||
private static String getRuleSetDescriptionSingleLine(RuleSet ruleset) {
|
||||
@ -315,7 +316,7 @@ public class RuleDocGenerator {
|
||||
description = StringEscapeUtils.escapeHtml4(description);
|
||||
description = description.replaceAll("\\n|\\r", " ");
|
||||
description = StringUtils.stripToEmpty(description);
|
||||
return description;
|
||||
return EscapeUtils.preserveRuleTagQuotes(description);
|
||||
}
|
||||
|
||||
private static List<String> toLines(String s) {
|
||||
|
@ -28,8 +28,10 @@ import java.util.regex.Pattern;
|
||||
public class RuleTagChecker {
|
||||
private static final Logger LOG = Logger.getLogger(DeadLinksChecker.class.getName());
|
||||
|
||||
private static final Pattern RULE_TAG = Pattern.compile("\\{%\\s*rule\\s+\"(.*?)\"\\s*");
|
||||
private static final Pattern RULE_REFERENCE = Pattern.compile("(\\w+)\\/(\\w+)\\/(\\w+)");
|
||||
private static final String QUOTE = "\"";
|
||||
private static final Pattern RULE_TAG = Pattern.compile("\\{%\\s*rule\\s+(\"?[^\"%\\}\\s]+\"?)\\s*");
|
||||
private static final Pattern RULE_REFERENCE = Pattern.compile("\"?(\\w+)\\/(\\w+)\\/(\\w+)\"?");
|
||||
private static final Pattern RULE_SIMPLE_REFERENCE = Pattern.compile("\"?(\\w+)\"?");
|
||||
|
||||
private final Path pagesDirectory;
|
||||
private final List<String> issues = new ArrayList<>();
|
||||
@ -72,6 +74,9 @@ public class RuleTagChecker {
|
||||
int pos = ruleTagMatcher.end();
|
||||
if (line.charAt(pos) != '%' || line.charAt(pos + 1) != '}') {
|
||||
addIssue(file, lineNo, "Rule tag for " + ruleReference + " is not closed properly");
|
||||
} else if (ruleReference.startsWith(QUOTE) && !ruleReference.endsWith(QUOTE)
|
||||
|| !ruleReference.startsWith(QUOTE) && ruleReference.endsWith(QUOTE)) {
|
||||
addIssue(file, lineNo, "Rule tag for " + ruleReference + " has a missing quote");
|
||||
} else if (!ruleReferenceTargetExists(ruleReference)) {
|
||||
addIssue(file, lineNo, "Rule " + ruleReference + " is not found");
|
||||
}
|
||||
@ -81,6 +86,7 @@ public class RuleTagChecker {
|
||||
|
||||
private boolean ruleReferenceTargetExists(String ruleReference) {
|
||||
Matcher ruleRefMatcher = RULE_REFERENCE.matcher(ruleReference);
|
||||
Matcher simpleRefMatcher = RULE_SIMPLE_REFERENCE.matcher(ruleReference);
|
||||
if (ruleRefMatcher.matches()) {
|
||||
String language = ruleRefMatcher.group(1);
|
||||
String category = ruleRefMatcher.group(2);
|
||||
@ -89,6 +95,9 @@ public class RuleTagChecker {
|
||||
Path ruleDocPage = pagesDirectory.resolve("pmd/rules/" + language + "/" + category.toLowerCase(Locale.ROOT) + ".md");
|
||||
Set<String> rules = getRules(ruleDocPage);
|
||||
return rules.contains(rule);
|
||||
} else if (simpleRefMatcher.matches()) {
|
||||
// can't check - would need to know the current language + category
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -18,12 +18,18 @@ public class RuleTagCheckerTest {
|
||||
RuleTagChecker checker = new RuleTagChecker(FileSystems.getDefault().getPath("src/test/resources/ruletagchecker"));
|
||||
List<String> issues = checker.check();
|
||||
|
||||
Assert.assertEquals(3, issues.size());
|
||||
Assert.assertEquals("ruletag-examples.md: 8: Rule tag for java/bestpractices/AvoidPrintStackTrace is not closed properly",
|
||||
Assert.assertEquals(7, issues.size());
|
||||
Assert.assertEquals("ruletag-examples.md: 9: Rule tag for \"java/bestpractices/AvoidPrintStackTrace\" is not closed properly",
|
||||
issues.get(0));
|
||||
Assert.assertEquals("ruletag-examples.md:10: Rule java/notexistingcategory/AvoidPrintStackTrace is not found",
|
||||
Assert.assertEquals("ruletag-examples.md:12: Rule \"java/notexistingcategory/AvoidPrintStackTrace\" is not found",
|
||||
issues.get(1));
|
||||
Assert.assertEquals("ruletag-examples.md:12: Rule java/bestpractices/NotExistingRule is not found",
|
||||
Assert.assertEquals("ruletag-examples.md:14: Rule \"java/bestpractices/NotExistingRule\" is not found",
|
||||
issues.get(2));
|
||||
Assert.assertEquals("ruletag-examples.md:16: Rule tag for \"java/bestpractices/OtherRule has a missing quote",
|
||||
issues.get(3));
|
||||
Assert.assertEquals("ruletag-examples.md:17: Rule tag for java/bestpractices/OtherRule\" has a missing quote",
|
||||
issues.get(4));
|
||||
Assert.assertEquals("ruletag-examples.md:21: Rule tag for \"OtherRule has a missing quote", issues.get(5));
|
||||
Assert.assertEquals("ruletag-examples.md:22: Rule tag for OtherRule\" has a missing quote", issues.get(6));
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ folder: pmd/rules
|
||||
|
||||
{% include callout.html content="Sample ruleset to test rule doc generation. Here might be <script>alert('XSS');</script> as well. And "quotes". Inside CDATA <script>alert('XSS');</script>." %}
|
||||
|
||||
* [DeprecatedSample](pmd_rules_java_sample.html#deprecatedsample): <span style="border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f; font-size: 75%;">Deprecated</span> Just some description of a deprecated rule.
|
||||
* [DeprecatedSample](pmd_rules_java_sample.html#deprecatedsample): <span style="border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f; font-size: 75%;">Deprecated</span> Just some description of a deprecated rule. RuleTag with quotes: Use {% rule "RenamedRule" %} ins...
|
||||
* [JumbledIncrementer](pmd_rules_java_sample.html#jumbledincrementer): Avoid jumbled loop incrementers - its usually a mistake, and is confusing even if intentional.
|
||||
* [MovedRule](pmd_rules_java_sample.html#movedrule): <span style="border-radius: 0.25em; color: #fff; padding: 0.2em 0.6em 0.3em; display: inline; background-color: #d9534f; font-size: 75%;">Deprecated</span> The rule has been moved to another ruleset. Use instead [JumbledIncrementer](pmd_rules_java_sample2.html#jumbledincrementer).
|
||||
* [OverrideBothEqualsAndHashcode](pmd_rules_java_sample.html#overridebothequalsandhashcode): Override both 'public boolean Object.equals(Object other)', and 'public int Object.hashCode()', o...
|
||||
|
@ -19,6 +19,14 @@ language: Java
|
||||
|
||||
Just some description of a deprecated rule.
|
||||
|
||||
RuleTag with quotes: Use {% rule "RenamedRule" %} instead.
|
||||
|
||||
RuleTag without quotes: Use {% rule RenamedRule %} instead.
|
||||
|
||||
RuleTag with full category and quotes: Use {% rule "java/sample/RenamedRule" %} instead.
|
||||
|
||||
RuleTag with full category and without quotes: Use {% rule java/sample/RenamedRule %} instead.
|
||||
|
||||
**This rule is defined by the following XPath expression:**
|
||||
``` xpath
|
||||
//ForStatement
|
||||
|
@ -194,6 +194,14 @@ public class JumbledIncrementerRule1 {
|
||||
deprecated="true">
|
||||
<description>
|
||||
Just some description of a deprecated rule.
|
||||
|
||||
RuleTag with quotes: Use {% rule "RenamedRule" %} instead.
|
||||
|
||||
RuleTag without quotes: Use {% rule RenamedRule %} instead.
|
||||
|
||||
RuleTag with full category and quotes: Use {% rule "java/sample/RenamedRule" %} instead.
|
||||
|
||||
RuleTag with full category and without quotes: Use {% rule java/sample/RenamedRule %} instead.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<properties>
|
||||
|
@ -4,9 +4,19 @@ permalink: rule_tag_samples.html
|
||||
---
|
||||
|
||||
This is a link to the rule AvoidPrintStackTrace: {% rule "java/bestpractices/AvoidPrintStackTrace" %}.
|
||||
This is a link to the rule AvoidPrintStackTrace without quotes: {% rule java/bestpractices/AvoidPrintStackTrace %}.
|
||||
|
||||
This is the same link, but the rule tag is not closed properly: {% rule "java/bestpractices/AvoidPrintStackTrace" %).
|
||||
|
||||
Now this is link to a rule inside a category, which doesn't exist: {% rule "java/notexistingcategory/AvoidPrintStackTrace" %}.
|
||||
Now this is link to a rule inside a category, which doesn't exist:
|
||||
{% rule "java/notexistingcategory/AvoidPrintStackTrace" %}.
|
||||
|
||||
This is link to a rule, which doesn't exist: {% rule "java/bestpractices/NotExistingRule" %}.
|
||||
|
||||
This is a rule tag, which has only one quote: {% rule "java/bestpractices/OtherRule %}.
|
||||
This is a rule tag, which has only one quote: {% rule java/bestpractices/OtherRule" %}.
|
||||
|
||||
This is a rule tag, that references a rule in the same category: {% rule OtherRule %} (correct).
|
||||
This is a rule tag, that references a rule in the same category: {% rule "OtherRule" %} (correct).
|
||||
This is a rule tag, that references a rule in the same category: {% rule "OtherRule %} (missing quote).
|
||||
This is a rule tag, that references a rule in the same category: {% rule OtherRule" %} (missing quote).
|
||||
|
@ -1059,7 +1059,7 @@ String name,
|
||||
Position literals first in comparisons, if the second argument is null then NullPointerExceptions
|
||||
can be avoided, they will just return false.
|
||||
|
||||
This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons %}.
|
||||
This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
@ -1084,7 +1084,7 @@ class Foo {
|
||||
Position literals first in comparisons, if the second argument is null then NullPointerExceptions
|
||||
can be avoided, they will just return false.
|
||||
|
||||
This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons %}.
|
||||
This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}.
|
||||
</description>
|
||||
<priority>3</priority>
|
||||
<example>
|
||||
|
Reference in New Issue
Block a user