diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java index fbb78a6898..75c56f7f49 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java @@ -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); 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 818ad3e7c8..97852a5c75 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 @@ -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 allRules = new HashMap<>(); + /** Caches ruleset to ruleset xml file mapping. */ + private final Map 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 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 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(rulesetFilename)) { - foundPathResult.add(file); - return FileVisitResult.TERMINATE; + private void determineRuleClassSourceFiles(Map> sortedRulesets) { + // first collect all the classes, we need to resolve and the rulesets + for (List 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() { + @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 foundPathResult = new LinkedList<>(); + if (path.contains("src")) { + String foundRuleClass = null; + for (Map.Entry 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() { - @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 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); } }