[doc] Improve performance of rule doc generator

Now we walk PMD source tree only once to resolve the
file names to rulesets and rules instead walking the
whole tree again for each ruleset/rule.
This commit is contained in:
Andreas Dangel
2020-07-23 12:39:19 +02:00
parent 6ee17d44f7
commit 8c06dbcd4c
2 changed files with 66 additions and 50 deletions

View File

@ -30,6 +30,11 @@ public final class GenerateRuleDocsCmd {
}
public static void main(String[] args) throws RuleSetNotFoundException {
if (args.length != 1) {
System.err.println("One argument is required: The base directory of the module pmd-doc.");
System.exit(1);
}
long start = System.currentTimeMillis();
Path output = FileSystems.getDefault().getPath(args[0]).resolve("..").toAbsolutePath().normalize();
System.out.println("Generating docs into " + output);

View File

@ -75,6 +75,12 @@ public class RuleDocGenerator {
private final Path root;
private final FileWriter writer;
/** Caches rule class name to java source file mapping. */
private final Map<String, String> allRules = new HashMap<>();
/** Caches ruleset to ruleset xml file mapping. */
private final Map<String, String> allRulesets = new HashMap<>();
public RuleDocGenerator(FileWriter writer, Path root) {
this.writer = Objects.requireNonNull(writer, "A file writer must be provided");
this.root = Objects.requireNonNull(root, "Root directory must be provided");
@ -91,6 +97,7 @@ public class RuleDocGenerator {
try {
sortedRulesets = sortRulesets(registeredRulesets);
sortedAdditionalRulesets = sortRulesets(resolveAdditionalRulesets(additionalRulesets));
determineRuleClassSourceFiles(sortedRulesets);
generateLanguageIndex(sortedRulesets, sortedAdditionalRulesets);
generateRuleSetIndex(sortedRulesets);
@ -345,7 +352,7 @@ public class RuleDocGenerator {
String permalink = RULESET_INDEX_PERMALINK_PATTERN
.replace("${language.tersename}", languageTersename)
.replace("${ruleset.name}", rulesetFilename);
String ruleSetSourceFilepath = "../" + getRuleSetSourceFilepath(ruleset);
String ruleSetSourceFilepath = "../" + allRulesets.get(ruleset.getFileName());
List<String> lines = new LinkedList<>();
lines.add("---");
@ -415,7 +422,7 @@ public class RuleDocGenerator {
} else {
lines.add("**This rule is defined by the following Java class:** "
+ "[" + rule.getRuleClass() + "]("
+ GITHUB_SOURCE_LINK + getRuleClassSourceFilepath(rule.getRuleClass())
+ GITHUB_SOURCE_LINK + allRules.get(rule.getRuleClass())
+ ")");
}
lines.add("");
@ -607,63 +614,67 @@ public class RuleDocGenerator {
}
/**
* Searches for the source file of the given ruleset. This provides the information
* for the "editme" link.
* Walks through the root directory once to get all rule source file path names and ruleset names.
* This provides the information for the "editme" links.
*
* @param ruleset the ruleset to search for.
* @return
* @throws IOException
* @param sortedRulesets all the rulesets and rules
*/
private String getRuleSetSourceFilepath(RuleSet ruleset) throws IOException {
final String rulesetFilename = FilenameUtils.normalize(StringUtils.chomp(ruleset.getFileName()));
final List<Path> foundPathResult = new LinkedList<>();
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
String path = file.toString();
if (path.contains("src") && path.endsWith(rulesetFilename)) {
foundPathResult.add(file);
return FileVisitResult.TERMINATE;
private void determineRuleClassSourceFiles(Map<Language, List<RuleSet>> sortedRulesets) {
// first collect all the classes, we need to resolve and the rulesets
for (List<RuleSet> rulesets : sortedRulesets.values()) {
for (RuleSet ruleset : rulesets) {
String rulesetFilename = FilenameUtils.normalize(StringUtils.chomp(ruleset.getFileName()));
allRulesets.put(ruleset.getFileName(), rulesetFilename);
for (Rule rule : ruleset.getRules()) {
String ruleClass = rule.getRuleClass();
String relativeSourceFilename = ruleClass.replaceAll("\\.", Matcher.quoteReplacement(File.separator))
+ ".java";
allRules.put(ruleClass, relativeSourceFilename);
}
return super.visitFile(file, attrs);
}
});
if (!foundPathResult.isEmpty()) {
Path foundPath = foundPathResult.get(0);
foundPath = root.relativize(foundPath);
// Note: the path is normalized to unix path separators, so that the editme link
// uses forward slashes
return FilenameUtils.normalize(foundPath.toString(), true);
}
return StringUtils.chomp(ruleset.getFileName());
}
// then go and search the actual files
try {
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
String path = file.toString();
private String getRuleClassSourceFilepath(String ruleClass) throws IOException {
final String relativeSourceFilename = ruleClass.replaceAll("\\.", Matcher.quoteReplacement(File.separator))
+ ".java";
final List<Path> foundPathResult = new LinkedList<>();
if (path.contains("src")) {
String foundRuleClass = null;
for (Map.Entry<String, String> entry : allRules.entrySet()) {
if (path.endsWith(entry.getValue())) {
foundRuleClass = entry.getKey();
break;
}
}
if (foundRuleClass != null) {
Path foundPath = root.relativize(file);
// Note: the path is normalized to unix path separators, so that the editme link
// uses forward slashes
allRules.put(foundRuleClass, FilenameUtils.normalize(foundPath.toString(), true));
}
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@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;
String foundRuleset = null;
for (Map.Entry<String, String> entry : allRulesets.entrySet()) {
if (path.endsWith(entry.getValue())) {
foundRuleset = entry.getKey();
break;
}
}
if (foundRuleset != null) {
Path foundPath = root.relativize(file);
// Note: the path is normalized to unix path separators, so that the editme link
// uses forward slashes
allRulesets.put(foundRuleset, FilenameUtils.normalize(foundPath.toString(), true));
}
}
return FileVisitResult.CONTINUE;
}
return super.visitFile(file, attrs);
}
});
if (!foundPathResult.isEmpty()) {
Path foundPath = foundPathResult.get(0);
foundPath = root.relativize(foundPath);
return FilenameUtils.normalize(foundPath.toString(), true);
});
} catch (IOException e) {
throw new RuntimeException(e);
}
return FilenameUtils.normalize(relativeSourceFilename, true);
}
}