forked from phoedos/pmd
[doc] Error in changelog 6.16.0 due to not properly closed rule tag
Fixes #1896
This commit is contained in:
@@ -51,6 +51,19 @@
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>check-rule-tags</id>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
<phase>verify</phase>
|
||||
<configuration>
|
||||
<mainClass>net.sourceforge.pmd.docs.RuleTagChecker</mainClass>
|
||||
<arguments>
|
||||
<argument>${project.basedir}</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.docs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
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 final Path pagesDirectory;
|
||||
private final List<String> issues = new ArrayList<>();
|
||||
private final Map<Path, Set<String>> rulesCache = new HashMap<>();
|
||||
|
||||
public RuleTagChecker(Path rootDirectory) {
|
||||
final Path pagesDirectory = rootDirectory.resolve("docs/pages");
|
||||
|
||||
if (!Files.isDirectory(pagesDirectory)) {
|
||||
LOG.severe("can't check rule tags, didn't find \"docs/pages\" directory at: " + pagesDirectory);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
this.pagesDirectory = pagesDirectory;
|
||||
}
|
||||
|
||||
public List<String> check() throws IOException {
|
||||
Files.walkFileTree(pagesDirectory, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
checkFile(file);
|
||||
return super.visitFile(file, attrs);
|
||||
}
|
||||
});
|
||||
return issues;
|
||||
}
|
||||
|
||||
private void checkFile(Path file) throws IOException {
|
||||
if (file == null || !file.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(".md")) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG.finer("Checking " + file);
|
||||
int lineNo = 0;
|
||||
for (String line : Files.readAllLines(file, StandardCharsets.UTF_8)) {
|
||||
lineNo++;
|
||||
Matcher ruleTagMatcher = RULE_TAG.matcher(line);
|
||||
while (ruleTagMatcher.find()) {
|
||||
String ruleReference = ruleTagMatcher.group(1);
|
||||
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 (!ruleReferenceTargetExists(ruleReference)) {
|
||||
addIssue(file, lineNo, "Rule " + ruleReference + " is not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean ruleReferenceTargetExists(String ruleReference) {
|
||||
Matcher ruleRefMatcher = RULE_REFERENCE.matcher(ruleReference);
|
||||
if (ruleRefMatcher.matches()) {
|
||||
String language = ruleRefMatcher.group(1);
|
||||
String category = ruleRefMatcher.group(2);
|
||||
String rule = ruleRefMatcher.group(3);
|
||||
|
||||
Path ruleDocPage = pagesDirectory.resolve("pmd/rules/" + language + "/" + category.toLowerCase(Locale.ROOT) + ".md");
|
||||
Set<String> rules = getRules(ruleDocPage);
|
||||
return rules.contains(rule);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<String> getRules(Path ruleDocPage) {
|
||||
Set<String> result = rulesCache.get(ruleDocPage);
|
||||
|
||||
if (result == null) {
|
||||
result = new HashSet<>();
|
||||
try {
|
||||
for (String line : Files.readAllLines(ruleDocPage, StandardCharsets.UTF_8)) {
|
||||
if (line.startsWith("## ")) {
|
||||
result.add(line.substring(3));
|
||||
}
|
||||
rulesCache.put(ruleDocPage, result);
|
||||
}
|
||||
} catch (NoSuchFileException e) {
|
||||
LOG.warning("File " + ruleDocPage + " not found.");
|
||||
} catch (IOException e) {
|
||||
LOG.log(Level.SEVERE, "Unable to read rules from " + ruleDocPage, e);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addIssue(Path file, int lineNo, String message) {
|
||||
issues.add(String.format("%s:%2d: %s", pagesDirectory.relativize(file).toString(), lineNo, message));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length != 1) {
|
||||
System.err.println("Wrong arguments!");
|
||||
System.err.println();
|
||||
System.err.println("java " + RuleTagChecker.class.getSimpleName() + " <project base directory>");
|
||||
System.exit(1);
|
||||
}
|
||||
final Path rootDirectory = Paths.get(args[0]).resolve("..").toRealPath();
|
||||
|
||||
RuleTagChecker ruleTagChecker = new RuleTagChecker(rootDirectory);
|
||||
List<String> issues = ruleTagChecker.check();
|
||||
|
||||
if (!issues.isEmpty()) {
|
||||
issues.forEach(System.err::println);
|
||||
throw new AssertionError("Wrong rule tags detected");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
|
||||
package net.sourceforge.pmd.docs;
|
||||
|
||||
import java.nio.file.FileSystems;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RuleTagCheckerTest {
|
||||
|
||||
@Test
|
||||
public void testAllChecks() throws Exception {
|
||||
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",
|
||||
issues.get(0));
|
||||
Assert.assertEquals("ruletag-examples.md:10: Rule java/notexistingcategory/AvoidPrintStackTrace is not found",
|
||||
issues.get(1));
|
||||
Assert.assertEquals("ruletag-examples.md:12: Rule java/bestpractices/NotExistingRule is not found",
|
||||
issues.get(2));
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
|
||||
## AvoidPrintStackTrace
|
||||
|
||||
Sample rule doc for tests.
|
@@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Sample Page with rule tags
|
||||
permalink: rule_tag_samples.html
|
||||
---
|
||||
|
||||
This is a link to the rule AvoidPrintStackTrace: {% 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" %}.
|
||||
|
||||
This is link to a rule, which doesn't exist: {% rule "java/bestpractices/NotExistingRule" %}.
|
Reference in New Issue
Block a user