package org.codefx.mvn.jdeps.rules; import org.codefx.mvn.jdeps.dependency.Type; /** * Judges the severity of individual dependencies according to predefined rules. * * <h2>Rules</h2> * Rules are specified during construction and generally take the form {@code (dependant -> dependency; severity)}. * The {@code dependent} as well as the {@code dependency} can be either a type or a package. * <p> * Given a pair of types, a rule is said to <em>match</em> if the first type belongs to its dependent and the second * type belongs to its dependency. "Belongs to" has a different meaning depending on whether the judge is "flat" or * "hierarchical" - see <em>Hierarchical vs. Flat</em> for details and examples - but a type always "belongs to" itself * and the package that contains it. * <p> * The <em>best match</em> for a pair of types is the rule that has the most specific dependant that contains the first * type and out of those partial matches defines the most specific dependency that contains the second. * <p> * Calling the judge with a pair of types returns either the severity specified by the best matching rule or, if no * rule matches, the default severity specified during construction. * * <h3>Example</h3> * Given the rules {@code A (com.foo -> sun.misc.Unsafe; WARN)}, {@code B (com.foo -> java.lang; WARN)}, and * {@code C (com.foo.Bar -> sun.misc; WARN)}: * <ul> * <li>for {@code com.foo.Foo -> sun.misc.Unsafe} the only match will be {@code A} because {@code Foo} does not belong * to {@code C}'s {@code Bar} and {@code Unsafe} does not belong to {@code B}'s {@code java.lang} * <li>for {@code com.foo.Bar -> sun.misc.Unsafe} the best match will be {@code C} because it has the most * specific dependent of the three rules and its dependency still matches {@code Unsafe} * <li>for {@code com.foo.Bar -> java.lang.String} the best match will be {@code B} because even though {@code C} * defines a more specific dependant (an exact match to be precise) it <em>does not</em> define a matching dependency * so it does not match as a whole * </ul> * * <h2>Hierarchical vs. Flat</h2> * * <h3>Flat Judge</h3> * A {@link PackageInclusion#FLAT flat} judge adheres to the official interpretation of Java packages, which are <em>not</em> hierarchical. * In this case there is no relation between, e.g., the packages "com.foo" and "com.foo.bar". * <p> * Rules defined for a package will only match the contained types, including their inner types. Rules defined for * types will match these types and the inner types. * * <h4>Example</h4> * Given the rule {@code (com.foo.Bar -> sun.misc.Unsafe; WARN)}: * <ul> * <li>{@code com.foo.Bar -> sun.misc.Unsafe} will match * <li>{@code com.foo.Bar.InnerClass -> sun.misc.Unsafe} will match because {@code InnerClass} belongs to {@code Bar} * <li>{@code com.foo.Baz -> sun.misc.Unsafe} will not match because {@code Baz} is not {@code Bar} * <li>{@code com.foo.Bar -> sun.misc.BASE64Decoder} will not match because {@code BASE64Decoder} is not {@code Unsafe} * </ul> * Given the rule {@code (com.foo -> sun.misc.Unsafe; WARN)}: * <ul> * <li>{@code com.foo.Bar -> sun.misc.Unsafe} will match because {@code Bar} is in the correct package * <li>{@code com.foo.Bar.InnerClass -> sun.misc.Unsafe} will match because {@code InnerClass} belongs to {@code Bar}, * which is in the correct package * <li>{@code com.foo.Baz -> sun.misc.Unsafe} will match because {@code Baz} is in the correct package * <li>{@code com.foo.Bar -> sun.misc.BASE64Decoder} will not match because {@code BASE64Decoder} is not {@code Unsafe} * <li>{@code com.foo.bar.Bar -> sun.misc.Unsafe} will not match because for a flat judge {@code com.foo.bar} does not * belong to {@code com.foo} * </ul> * * <h3>Hierarchical Judge</h3> * A {@link PackageInclusion#HIERARCHICAL hierarchical} judge will interpret package names similar to folders and thus create a relation where packages can * contain other packages, e.g. {@code sun} contains {@code sun.misc}. * * <h4>Example</h4> * Given the rule {@code (com.foo.Bar -> sun.misc.Unsafe; WARN)}: * <ul> * <li>{@code com.foo.Bar -> sun.misc.Unsafe} will match * <li>{@code com.foo.Bar.InnerClass -> sun.misc.Unsafe} will match because {@code InnerClass} belongs to {@code Bar} * <li>{@code com.foo.Baz -> sun.misc.Unsafe} will not match because {@code Baz} is not {@code Bar} * <li>{@code com.foo.Bar -> sun.misc.BASE64Decoder} will not match because {@code BASE64Decoder} is not {@code Unsafe} * </ul> * Given the rule {@code (com.foo -> sun.misc.Unsafe; WARN)}: * <ul> * <li>{@code com.foo.Bar -> sun.misc.Unsafe} will match because {@code Bar} is in the correct package * <li>{@code com.foo.Bar.InnerClass -> sun.misc.Unsafe} will match because {@code InnerClass} belongs to {@code Bar}, * which is in the correct package * <li>{@code com.foo.Baz -> sun.misc.Unsafe} will match because {@code Baz} is in the correct package * <li>{@code com.foo.Bar -> sun.misc.BASE64Decoder} will not match because {@code BASE64Decoder} is not {@code Unsafe} * <li>{@code com.foo.bar.Bar -> sun.misc.Unsafe} will match because for a hierarchical judge {@code com.foo.bar} * belongs to {@code com.foo} * </ul> */ public interface DependencyJudge { /** * Indicates the severity of the specified dependency {@code dependent -> dependency}. * * @param dependent * the type which depends on the the other type * @param dependency * the type upon which the {@code dependent} depends * * @return the severity of the dependency */ default Severity judgeSeverity(Type dependent, Type dependency) { return judgeSeverity(dependent.getFullyQualifiedName(), dependency.getFullyQualifiedName()); } /** * Indicates the severity of the specified dependency. * * @param dependentName * fully qualified name of the type or package which depends on the the other * @param dependencyName * fully qualified name of the type or package upon which the {@code dependent} depends * * @return the severity of the dependency */ Severity judgeSeverity(String dependentName, String dependencyName); }