builder: refactoring to reduce complexity

This commit is contained in:
Paul Campbell 2018-03-11 16:24:41 +00:00
parent de5530e696
commit 77394ad5d3
3 changed files with 142 additions and 49 deletions

View file

@ -31,6 +31,7 @@
<map-builder.version>1.0.0</map-builder.version> <map-builder.version>1.0.0</map-builder.version>
<mockito.version>2.13.0</mockito.version> <mockito.version>2.13.0</mockito.version>
<assertj.version>3.9.0</assertj.version> <assertj.version>3.9.0</assertj.version>
<conditional.version>0.3.0</conditional.version>
<fast-classpath-scanner.version>2.18.1</fast-classpath-scanner.version> <fast-classpath-scanner.version>2.18.1</fast-classpath-scanner.version>
<maven-checkstyle-plugin.version>3.0.0</maven-checkstyle-plugin.version> <maven-checkstyle-plugin.version>3.0.0</maven-checkstyle-plugin.version>
@ -59,6 +60,11 @@
<version>${lombok.version}</version> <version>${lombok.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>net.kemitix</groupId>
<artifactId>conditional</artifactId>
<version>${conditional.version}</version>
</dependency>
<dependency> <dependency>
<groupId>io.github.lukehutch</groupId> <groupId>io.github.lukehutch</groupId>
<artifactId>fast-classpath-scanner</artifactId> <artifactId>fast-classpath-scanner</artifactId>

View file

@ -21,21 +21,22 @@
package net.kemitix.checkstyle.ruleset.builder; package net.kemitix.checkstyle.ruleset.builder;
import com.speedment.common.mapstream.MapStream;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.Arrays; import java.nio.file.Path;
import java.util.Map; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static java.util.Arrays.asList;
/** /**
* Writes the Checkstyle XML files. * Writes the Checkstyle XML files.
* *
@ -46,8 +47,6 @@ import java.util.stream.Stream;
@RequiredArgsConstructor @RequiredArgsConstructor
class CheckstyleWriter implements CommandLineRunner { class CheckstyleWriter implements CommandLineRunner {
private static final String NEWLINE = System.getProperty("line.separator");
private final OutputProperties outputProperties; private final OutputProperties outputProperties;
private final TemplateProperties templateProperties; private final TemplateProperties templateProperties;
@ -56,62 +55,79 @@ class CheckstyleWriter implements CommandLineRunner {
private final RuleClassLocator ruleClassLocator; private final RuleClassLocator ruleClassLocator;
private static Predicate<RuleLevel> 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 @Override
public void run(final String... args) { public void run(final String... args) {
Stream.of(RuleLevel.values()) Stream.of(RuleLevel.values())
.filter(level -> !level.equals(RuleLevel.UNSPECIFIED)) .filter(excludeUnspecifiedRuleLevel())
.forEach(this::writeCheckstyleFile); .forEach(this::writeCheckstyleFile);
} }
private void writeCheckstyleFile(final RuleLevel ruleLevel) { private void writeCheckstyleFile(final RuleLevel ruleLevel) {
val xmlFile = outputProperties.getDirectory() final Path outputPath = outputPath(ruleLevel);
.resolve(outputProperties.getRulesetFiles() final String checkerRules = enabledRules(ruleLevel, RuleParent.CHECKER);
.get(ruleLevel)); final String treeWalkerRules = enabledRules(ruleLevel, RuleParent.TREEWALKER);
val checkerRules = rulesProperties.getRules() final Path template = templateProperties.getCheckstyleXml();
.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));
try { try {
val checkstyleXmlTemplate = templateProperties.getCheckstyleXml(); writeCheckstyleFileWithException(outputPath, checkerRules, treeWalkerRules, template);
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);
}
} catch (IOException e) { } catch (IOException e) {
throw new CheckstyleWriterException(e); throw new CheckstyleWriterException(e);
} }
} }
private String formatRuleAsModule(final Rule rule) { private Path outputPath(final RuleLevel ruleLevel) {
if (rule.getProperties() return outputProperties.getDirectory()
.isEmpty()) { .resolve(outputProperties.getRulesetFiles()
return String.format("<module name=\"%s\"/>", ruleClassLocator.apply(rule)); .get(ruleLevel));
}
return String.format("<module name=\"%s\">%n %s%n</module>", ruleClassLocator.apply(rule),
formatProperties(rule.getProperties())
);
} }
private String formatProperties(final Map<String, String> properties) { private String enabledRules(
return MapStream.of(properties) final RuleLevel ruleLevel,
.map((k, v) -> String.format("<property name=\"%s\" value=\"%s\"/>", k, v)) final RuleParent ruleParent
.collect(Collectors.joining(NEWLINE)); ) {
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()));
} }
} }

View file

@ -21,13 +21,17 @@
package net.kemitix.checkstyle.ruleset.builder; package net.kemitix.checkstyle.ruleset.builder;
import com.speedment.common.mapstream.MapStream;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.kemitix.conditional.Value;
import java.net.URI; import java.net.URI;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/** /**
* A single Checkstyle Check. * A single Checkstyle Check.
@ -40,6 +44,10 @@ public class Rule {
private static final Locale LOCALE = Locale.ENGLISH; private static final Locale LOCALE = Locale.ENGLISH;
private static final String MODULE_NO_PROPERTIES = "<module name=\"%s\"/>";
private static final String MODULE_WITH_PROPERTIES = "<module name=\"%s\">%n %s%n</module>";
/** /**
* Configuration properties. * Configuration properties.
*/ */
@ -100,6 +108,69 @@ public class Rule {
.compareTo(right.getLowerCaseRuleName()); .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<Rule> 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<Rule> isIncludedInLevel(final RuleLevel ruleLevel) {
return rule -> ruleLevel.compareTo(rule.getLevel()) >= 0;
}
private static String formatProperties(final Map<String, String> properties) {
return MapStream.of(properties)
.map((k, v) -> String.format("<property name=\"%s\" value=\"%s\"/>", 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 <module/>} fragment
*/
static String asModule(
final String ruleClassname,
final Rule rule
) {
return Value.<String>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() { private String getLowerCaseRuleName() {
return getName().toLowerCase(LOCALE); return getName().toLowerCase(LOCALE);
} }