package org.codefx.mvn.jdeps.rules; import java.util.Objects; import static java.lang.String.format; public final class DependencyRule { public static final String ALL_TYPES_WILDCARD = "*"; private static final String ERROR_MESSAGE_MISSING_TYPE = "The rule %s defines no %s."; private static final String ERROR_MESSAGE_MISSING_SEVERITY = "The rule %s defines no severity."; private static final String ERROR_MESSAGE_NAME_PART_EMPTY = "In the rule %s the name '%s' contains one empty part. Make sure it has no superfluous dots."; private static final String ERROR_MESSAGE_NAME_PART_STARTS_INVALID = "In the rule %s a part of the name '%s' starts with the invalid character '%s'."; private static final String ERROR_MESSAGE_NAME_PART_CONTAINS_INVALID = "In the rule %s the name '%s' contains the invalid character '%s'."; private static final String ERROR_MESSAGE_INVALID_SEVERITY = "The rule %s defines an invalid severity."; private final String dependent; private final String dependency; private final Severity severity; private DependencyRule(String dependent, String dependency, Severity severity) { this.dependent = dependent; this.dependency = dependency; this.severity = severity; } public static DependencyRule of(String dependent, String dependency, String severity) { Severity asSeverity = parseSeverity(dependent, dependency, severity); return of(dependent, dependency, asSeverity); } private static Severity parseSeverity(String dependent, String dependency, String severity) { if (severity == null) throw new IllegalArgumentException( format(ERROR_MESSAGE_MISSING_SEVERITY, toString(dependent, dependency, (String) null))); try { return Severity.valueOf(severity); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException( format(ERROR_MESSAGE_INVALID_SEVERITY, toString(dependent, dependency, severity))); } } public static DependencyRule of(String dependent, String dependency, Severity severity) { checkName(dependent, toString(dependent, dependency, severity), "dependent"); checkName(dependency, toString(dependent, dependency, severity), "dependency"); if (severity == null) throw new IllegalArgumentException( format(ERROR_MESSAGE_MISSING_SEVERITY, toString(dependent, dependency, (String) null))); return new DependencyRule(dependent, dependency, severity); } /* * If only 'checkValidity()' were accessible, we would have to have the same checks for the dependent and the * dependency. To prevent this useless repetition 'DependencyRule' exposes 'checkName'. */ /** * Checks whether the specified name is a valid Java identifier for a package or class. * * @param name * the name to check * @param ruleAsString * a textual representation of the rule so that it can be used in the error output * @param role * the role of the type called {@code name}, i.e. dependent or dependency * * @throws IllegalArgumentException * if the name is invalid */ static void checkName(String name, String ruleAsString, String role) throws IllegalArgumentException { if (name == null || name.isEmpty()) throw new IllegalArgumentException(format(ERROR_MESSAGE_MISSING_TYPE, ruleAsString, role)); if (name.equals(ALL_TYPES_WILDCARD)) return; for (String namePart : name.split("\\.")) { if (namePart == null || namePart.isEmpty()) throw new IllegalArgumentException( format(ERROR_MESSAGE_NAME_PART_EMPTY, ruleAsString, name)); if (!Character.isJavaIdentifierStart(namePart.charAt(0))) throw new IllegalArgumentException( format(ERROR_MESSAGE_NAME_PART_STARTS_INVALID, ruleAsString, name, namePart.charAt(0))); for (int i = 1; i < namePart.length(); i++) if (!Character.isJavaIdentifierPart(namePart.charAt(i))) throw new IllegalArgumentException( format(ERROR_MESSAGE_NAME_PART_CONTAINS_INVALID, ruleAsString, name, namePart.charAt(i))); } // split does not include trailing empty strings so make an extra check for string ending with "." if (name.endsWith(".")) throw new IllegalArgumentException( format(ERROR_MESSAGE_NAME_PART_EMPTY, ruleAsString, name)); } public String getDependent() { return dependent; } public String getDependency() { return dependency; } public Severity getSeverity() { return severity; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof DependencyRule)) return false; DependencyRule that = (DependencyRule) o; return Objects.equals(dependent, that.dependent) && Objects.equals(dependency, that.dependency); } @Override public int hashCode() { return Objects.hash(dependent, dependency); } @Override public String toString() { return toString(dependent, dependency, severity); } private static String toString(String dependent, String dependency, Severity severity) { return toString(dependent, dependency, severity == null ? "null" : severity.toString()); } private static String toString(String dependent, String dependency, String severity) { return format("(%s -> %s: %s)", dependent, dependency, severity); } }