diff --git a/builder/pom.xml b/builder/pom.xml index 18f07d5..73d39c3 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -31,6 +31,7 @@ 1.0.0 2.13.0 3.9.0 + 0.3.0 2.18.1 3.0.0 @@ -59,6 +60,11 @@ ${lombok.version} provided + + net.kemitix + conditional + ${conditional.version} + io.github.lukehutch fast-classpath-scanner diff --git a/builder/src/main/java/net/kemitix/checkstyle/ruleset/builder/CheckstyleWriter.java b/builder/src/main/java/net/kemitix/checkstyle/ruleset/builder/CheckstyleWriter.java index 5c9e137..54a55d9 100644 --- a/builder/src/main/java/net/kemitix/checkstyle/ruleset/builder/CheckstyleWriter.java +++ b/builder/src/main/java/net/kemitix/checkstyle/ruleset/builder/CheckstyleWriter.java @@ -21,21 +21,22 @@ package net.kemitix.checkstyle.ruleset.builder; -import com.speedment.common.mapstream.MapStream; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import lombok.val; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; +import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.util.Arrays; -import java.util.Map; +import java.nio.file.Path; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.util.Arrays.asList; + /** * Writes the Checkstyle XML files. * @@ -46,8 +47,6 @@ import java.util.stream.Stream; @RequiredArgsConstructor class CheckstyleWriter implements CommandLineRunner { - private static final String NEWLINE = System.getProperty("line.separator"); - private final OutputProperties outputProperties; private final TemplateProperties templateProperties; @@ -56,62 +55,79 @@ class CheckstyleWriter implements CommandLineRunner { private final RuleClassLocator ruleClassLocator; + private static Predicate excludeUnspecifiedRuleLevel() { + return level -> !level.equals(RuleLevel.UNSPECIFIED); + } + + private static void writeRuleset( + final Path filePath, + final String content + ) throws IOException { + Files.write(filePath, asList(content.split(System.lineSeparator())), StandardCharsets.UTF_8); + } + + private static String ruleset( + final String checkerRules, + final String treeWalkerRules, + final String template + ) { + return String.format(template, checkerRules, treeWalkerRules); + } + + private static boolean fileExists(final File file) { + return file.exists(); + } + + private static void writeCheckstyleFileWithException( + final Path outputPath, + final String checkerRules, + final String treeWalkerRules, + final Path template + ) throws IOException { + if (fileExists(template.toFile())) { + log.info("Writing ruleset: {}", outputPath); + final String xmlTemplate = new String(Files.readAllBytes(template), StandardCharsets.UTF_8); + writeRuleset(outputPath, ruleset(checkerRules, treeWalkerRules, xmlTemplate)); + } else { + throw new TemplateNotFoundException(template); + } + } + @Override public void run(final String... args) { Stream.of(RuleLevel.values()) - .filter(level -> !level.equals(RuleLevel.UNSPECIFIED)) - .forEach(this::writeCheckstyleFile); + .filter(excludeUnspecifiedRuleLevel()) + .forEach(this::writeCheckstyleFile); } private void writeCheckstyleFile(final RuleLevel ruleLevel) { - val xmlFile = outputProperties.getDirectory() - .resolve(outputProperties.getRulesetFiles() - .get(ruleLevel)); - val checkerRules = rulesProperties.getRules() - .stream() - .filter(Rule::isEnabled) - .filter(rule -> RuleParent.CHECKER.equals(rule.getParent())) - .filter(rule -> ruleLevel.compareTo(rule.getLevel()) >= 0) - .map(this::formatRuleAsModule) - .collect(Collectors.joining(NEWLINE)); - val treeWalkerRules = rulesProperties.getRules() - .stream() - .filter(Rule::isEnabled) - .filter(rule -> RuleParent.TREEWALKER.equals(rule.getParent())) - .filter(rule -> ruleLevel.compareTo(rule.getLevel()) >= 0) - .map(this::formatRuleAsModule) - .collect(Collectors.joining(NEWLINE)); + final Path outputPath = outputPath(ruleLevel); + final String checkerRules = enabledRules(ruleLevel, RuleParent.CHECKER); + final String treeWalkerRules = enabledRules(ruleLevel, RuleParent.TREEWALKER); + final Path template = templateProperties.getCheckstyleXml(); try { - val checkstyleXmlTemplate = templateProperties.getCheckstyleXml(); - if (checkstyleXmlTemplate.toFile() - .exists()) { - val bytes = Files.readAllBytes(checkstyleXmlTemplate); - val template = new String(bytes, StandardCharsets.UTF_8); - val output = Arrays.asList(String.format(template, checkerRules, treeWalkerRules) - .split(NEWLINE)); - log.info("Writing xmlFile: {}", xmlFile); - Files.write(xmlFile, output, StandardCharsets.UTF_8); - } else { - throw new TemplateNotFoundException(checkstyleXmlTemplate); - } + writeCheckstyleFileWithException(outputPath, checkerRules, treeWalkerRules, template); } catch (IOException e) { throw new CheckstyleWriterException(e); } } - private String formatRuleAsModule(final Rule rule) { - if (rule.getProperties() - .isEmpty()) { - return String.format("", ruleClassLocator.apply(rule)); - } - return String.format("%n %s%n", ruleClassLocator.apply(rule), - formatProperties(rule.getProperties()) - ); + private Path outputPath(final RuleLevel ruleLevel) { + return outputProperties.getDirectory() + .resolve(outputProperties.getRulesetFiles() + .get(ruleLevel)); } - private String formatProperties(final Map properties) { - return MapStream.of(properties) - .map((k, v) -> String.format("", k, v)) - .collect(Collectors.joining(NEWLINE)); + private String enabledRules( + final RuleLevel ruleLevel, + final RuleParent ruleParent + ) { + return rulesProperties.getRules() + .stream() + .filter(Rule::isEnabled) + .filter(Rule.hasParent(ruleParent)) + .filter(Rule.isIncludedInLevel(ruleLevel)) + .map(rule -> Rule.asModule(ruleClassLocator.apply(rule), rule)) + .collect(Collectors.joining(System.lineSeparator())); } } diff --git a/builder/src/main/java/net/kemitix/checkstyle/ruleset/builder/Rule.java b/builder/src/main/java/net/kemitix/checkstyle/ruleset/builder/Rule.java index 33c5624..30848f7 100644 --- a/builder/src/main/java/net/kemitix/checkstyle/ruleset/builder/Rule.java +++ b/builder/src/main/java/net/kemitix/checkstyle/ruleset/builder/Rule.java @@ -21,13 +21,17 @@ package net.kemitix.checkstyle.ruleset.builder; +import com.speedment.common.mapstream.MapStream; import lombok.Getter; import lombok.Setter; +import net.kemitix.conditional.Value; import java.net.URI; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** * A single Checkstyle Check. @@ -40,6 +44,10 @@ public class Rule { private static final Locale LOCALE = Locale.ENGLISH; + private static final String MODULE_NO_PROPERTIES = ""; + + private static final String MODULE_WITH_PROPERTIES = "%n %s%n"; + /** * Configuration properties. */ @@ -100,6 +108,69 @@ public class Rule { .compareTo(right.getLowerCaseRuleName()); } + /** + * Predicate to check that the Rule has the given RuleParent. + * + * @param ruleParent the RuleParent to match + * + * @return a Predicate to check a Rule + */ + static Predicate hasParent(final RuleParent ruleParent) { + return rule -> ruleParent.equals(rule.getParent()); + } + + + /** + * Predicate to check that the Rule is included in the given RuleLevel. + * + * @param ruleLevel the RuleLevel to match + * + * @return a Predicate to check a Rule + */ + static Predicate isIncludedInLevel(final RuleLevel ruleLevel) { + return rule -> ruleLevel.compareTo(rule.getLevel()) >= 0; + } + + private static String formatProperties(final Map properties) { + return MapStream.of(properties) + .map((k, v) -> String.format("", k, v)) + .collect(Collectors.joining(System.lineSeparator())); + } + + private static boolean hasProperties(final Rule rule) { + return !rule.getProperties().isEmpty(); + } + + /** + * Formats the Rule as an XML module fragment. + * + * @param rule the Rule to format + * @param ruleClassname the classname for the Rule + * + * @return an XML {@code } fragment + */ + static String asModule( + final String ruleClassname, + final Rule rule + ) { + return Value.where(hasProperties(rule)) + .then(() -> moduleWithParameters(rule, ruleClassname)) + .otherwise(() -> moduleNoParameters(ruleClassname)); + } + + private static String moduleNoParameters( + final String ruleClassname + ) { + return String.format(MODULE_NO_PROPERTIES, ruleClassname); + } + + private static String moduleWithParameters( + final Rule rule, + final String ruleClassname + ) { + return String.format(MODULE_WITH_PROPERTIES, ruleClassname, formatProperties(rule.getProperties())); + } + private String getLowerCaseRuleName() { return getName().toLowerCase(LOCALE); }