From ea9a15462130c5c74c138f6020dc5c8481a1241d Mon Sep 17 00:00:00 2001 From: Baron Roberts Date: Thu, 27 Jun 2024 23:02:13 -0700 Subject: [PATCH] Implement the "complement" and "isNotEmpty" methods. Provide examples in the Javadoc for the "intersect", "union", and "difference" methods. --- .../versionparser/VersionConstraint.java | 72 ++++++++++++++++++- .../versionparser/VersionConstraintTest.java | 33 +++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cthing/versionparser/VersionConstraint.java b/src/main/java/org/cthing/versionparser/VersionConstraint.java index 1ea1d89..0f0956d 100644 --- a/src/main/java/org/cthing/versionparser/VersionConstraint.java +++ b/src/main/java/org/cthing/versionparser/VersionConstraint.java @@ -139,6 +139,15 @@ public boolean isEmpty() { return this.ranges.isEmpty(); } + /** + * Indicates whether this constraint contains any versions. + * + * @return {@code true} if this constraint contains at least one version. + */ + public boolean isNotEmpty() { + return !this.ranges.isEmpty(); + } + /** * Indicates whether this constraint allows all versions. * @@ -175,7 +184,7 @@ public boolean isSingleVersion() { * @return {@code true} if the specified version is allowed by this constraint. */ public boolean allows(final Version version) { - return !isEmpty() && this.ranges.stream().anyMatch(range -> range.allows(version)); + return isNotEmpty() && this.ranges.stream().anyMatch(range -> range.allows(version)); } /** @@ -245,6 +254,21 @@ public boolean allowsAny(final VersionConstraint other) { /** * Creates a constraint that only allows versions allowed by both this and the specified constraints. + * The following examples show the intersection of this constraint T and the specified constraint C: + *
+     * T = []                    C = []                    T ∩ C = []
+     * T = []                    C = (,)                   T ∩ C = []
+     * T = [1.5]                 C = []                    T ∩ C = []
+     * T = [1.5,2.0]             C = [1.7]                 T ∩ C = [1.7]
+     * T = [1.5,2.0]             C = [1.6,1.9]             T ∩ C = [1.6,1.9]
+     * T = [1.5,)                C = [1.2,1.8]             T ∩ C = [1.5,1.8]
+     * T = (,3.0)                C = (,1.8]                T ∩ C = (,1.8]
+     * T = [1.5,2.0)             C = [1.6],[1.8]           T ∩ C = [1.6],[1.8]
+     * T = [1.5,2.0),[3.0,4.0)   C = []                    T ∩ C = []
+     * T = [1.5,2.0),[3.0,4.0)   C = [1.6,1.6]             T ∩ C = [1.6,1.6]
+     * T = [1.5,2.0),[3.0,4.0)   C = [1.6,1.8]             T ∩ C = [1.6,1.8]
+     * T = [1.5,2.0),[3.0,4.0)   C = [1.6,1.8],[3.2,3.4]   T ∩ C = [1.6,1.8],[3.2,3.4]
+     * 
* * @param other Constraint to intersect with this constraint * @return A new constraint that only allows versions allowed by both this and the specified constraints. @@ -285,6 +309,21 @@ public VersionConstraint intersect(final VersionConstraint other) { /** * Creates a constraint that allows versions allowed by either this or the specified constraint. + * The following examples show the union of this constraint T and the specified constraint C: + *
+     * T = []                    C = []          T ∪ C = []
+     * T = []                    C= (,)          T ∪ C = (,)
+     * T = [1.5]                 C = []          T ∪ C = [1.5]
+     * T = [1.5]                 C = [1.5]       T ∪ C = [1.5]
+     * T = [1.5]                 C = [1.6]       T ∪ C = [1.5],[1.6]
+     * T = [1.5,1.7)             C = [1.6]       T ∪ C = [1.5,1.7)
+     * T = (1.5,)                C = [1.3,2.0]   T ∪ C = [1.3,)
+     * T = (,1.7]                C = [1.5,2.0]   T ∪ C = (,2.0]
+     * T = (,)                   C = [1.5,2.0]   T ∪ C = (,)
+     * T = [1.2,2.0)             C = (,)         T ∪ C = (,)
+     * T = (1.0,2.0)             C = (2.0,3.0)   T ∪ C = (1.0,2.0),(2.0,3.0)
+     * T = [1.5,2.0),[3.0,4.0)   C = [1.6,1.6]   T ∪ C = [1.5,2.0),[3.0,4.0)
+     * 
* * @param other Constraint to for a union with this constraint * @return A new constraint that allows versions allowed by either this or the specified constraint. @@ -327,6 +366,20 @@ public VersionConstraint union(final VersionConstraint other) { /** * Creates a constraint that allows versions allowed by this constraint but not by the specified constraint. + * The following examples show the difference between this constraint T and the specified constraint C: + *
+     * T = [1.5]                 C = []                    T - C = [1.5]
+     * T = [1.5]                 C = [1.5]                 T - C = []
+     * T = [1.2,2.0)             C = [1.5,1.5]             T - C = [1.2,1.5),(1.5,2.0)
+     * T = [1.2,2.0)             C = [1.2,1.2]             T - C = (1.2,2.0)
+     * T = [1.2,2.0)             C = (,)                   T - C = []
+     * T = [1.2,2.0)             C = [1.5,1.5],[1.7,1.7]   T - C = [1.2,1.5),(1.5,1.7),(1.7,2.0)
+     * T = [1.5],[2.0,3.0)       C = []                    T - C = [1.5],[2.0,3.0)
+     * T = [1.5],[2.0,3.0)       C = [1.5,1.5]             T - C = [2.0,3.0)
+     * T = [1.0,3.0),(4.0,6.0)   C = [2.0],[7.0,8.0)       T - C = [1.0,2.0),(2.0,3.0),(4.0,6.0)
+     * T = []                    C = []                    T - C = []
+     * T = []                    C = [1.2,2.0)             T - C = []
+     * 
* * @param other Constraint to form the difference with this constraint * @return A new constraint that allows versions allowed by this constraint but not by the specified constraint. @@ -433,6 +486,23 @@ public VersionConstraint difference(final VersionConstraint other) { return differenceRanges.isEmpty() ? EMPTY : new VersionConstraint(differenceRanges); } + /** + * Creates a constraint that is the complement of this constraint. The following examples show the constraint C + * and its complement C': + *
+     * C = []                          C' = (,)
+     * C = [1.5]                       C' = (,1.5),(1.5,)
+     * C = [1.2,2.0)                   C' = (,1.2),[2.0,)
+     * C = [1.5],[2.0,3.0)             C' = (,1.5),(1.5,2.0),[3.0,)
+     * C = [1.5],[2.0,3.0),[4.0,7.0)   C' = (,1.5),(1.5,2.0),[3.0,4.0),[7.0,)
+     * 
+ * + * @return Complement of this constraint. + */ + public VersionConstraint complement() { + return ANY.difference(this); + } + @Override public boolean equals(final Object obj) { if (this == obj) { diff --git a/src/test/java/org/cthing/versionparser/VersionConstraintTest.java b/src/test/java/org/cthing/versionparser/VersionConstraintTest.java index 89fef65..596e8b7 100644 --- a/src/test/java/org/cthing/versionparser/VersionConstraintTest.java +++ b/src/test/java/org/cthing/versionparser/VersionConstraintTest.java @@ -27,6 +27,7 @@ public class VersionConstraintTest { @Test public void testAny() throws VersionParsingException { assertThat(ANY.isEmpty()).isFalse(); + assertThat(ANY.isNotEmpty()).isTrue(); assertThat(ANY.isAny()).isTrue(); assertThat(ANY.isWeak()).isFalse(); assertThat(ANY.isSingleVersion()).isFalse(); @@ -45,6 +46,7 @@ public void testEmpty() { when(constraint.isEmpty()).thenReturn(true); assertThat(EMPTY.isEmpty()).isTrue(); + assertThat(EMPTY.isNotEmpty()).isFalse(); assertThat(EMPTY.isAny()).isFalse(); assertThat(EMPTY.isWeak()).isFalse(); assertThat(EMPTY.isSingleVersion()).isFalse(); @@ -63,6 +65,7 @@ public void testConstructionSingleVersion() { VersionConstraint constraint = new VersionConstraint(version); assertThat(constraint.isEmpty()).isFalse(); + assertThat(constraint.isNotEmpty()).isTrue(); assertThat(constraint.isAny()).isFalse(); assertThat(constraint.isWeak()).isFalse(); assertThat(constraint.isSingleVersion()).isTrue(); @@ -70,6 +73,7 @@ public void testConstructionSingleVersion() { constraint = new VersionConstraint(version, true); assertThat(constraint.isEmpty()).isFalse(); + assertThat(constraint.isNotEmpty()).isTrue(); assertThat(constraint.isAny()).isFalse(); assertThat(constraint.isWeak()).isTrue(); assertThat(constraint.isSingleVersion()).isTrue(); @@ -82,6 +86,7 @@ public void testConstructionSingleRange() { final Version version2 = version("3.0"); VersionConstraint constraint = new VersionConstraint(version1, version2, true, false); assertThat(constraint.isEmpty()).isFalse(); + assertThat(constraint.isNotEmpty()).isTrue(); assertThat(constraint.isAny()).isFalse(); assertThat(constraint.isWeak()).isFalse(); assertThat(constraint.isSingleVersion()).isFalse(); @@ -90,6 +95,7 @@ public void testConstructionSingleRange() { final Version version3 = version("1.2.3"); constraint = new VersionConstraint(version3, version3, false, false); assertThat(constraint.isEmpty()).isFalse(); + assertThat(constraint.isNotEmpty()).isTrue(); assertThat(constraint.isAny()).isFalse(); assertThat(constraint.isWeak()).isFalse(); assertThat(constraint.isSingleVersion()).isFalse(); @@ -97,6 +103,7 @@ public void testConstructionSingleRange() { constraint = new VersionConstraint(version3, version3, false, false, true); assertThat(constraint.isEmpty()).isFalse(); + assertThat(constraint.isNotEmpty()).isTrue(); assertThat(constraint.isAny()).isFalse(); assertThat(constraint.isWeak()).isTrue(); assertThat(constraint.isSingleVersion()).isFalse(); @@ -111,6 +118,7 @@ public void testConstructionMultipleRanges() { VersionConstraint constraint = new VersionConstraint(List.of(range2, range1)); assertThat(constraint.isEmpty()).isFalse(); + assertThat(constraint.isNotEmpty()).isTrue(); assertThat(constraint.isAny()).isFalse(); assertThat(constraint.isWeak()).isFalse(); assertThat(constraint.isSingleVersion()).isFalse(); @@ -118,6 +126,7 @@ public void testConstructionMultipleRanges() { constraint = new VersionConstraint(List.of(range2, range1), true); assertThat(constraint.isEmpty()).isFalse(); + assertThat(constraint.isNotEmpty()).isTrue(); assertThat(constraint.isAny()).isFalse(); assertThat(constraint.isWeak()).isTrue(); assertThat(constraint.isSingleVersion()).isFalse(); @@ -125,6 +134,7 @@ public void testConstructionMultipleRanges() { constraint = new VersionConstraint(List.of(range3)); assertThat(constraint.isEmpty()).isFalse(); + assertThat(constraint.isNotEmpty()).isTrue(); assertThat(constraint.isAny()).isFalse(); assertThat(constraint.isWeak()).isFalse(); assertThat(constraint.isSingleVersion()).isTrue(); @@ -304,6 +314,11 @@ public void testAllowsAny() throws VersionParsingException { @Test public void testIntersect() throws VersionParsingException { + assertThat(EMPTY.intersect(EMPTY)).isEmpty(); + assertThat(EMPTY.intersect(ANY)).isEmpty(); + assertThat(ANY.intersect(EMPTY)).isEmpty(); + assertThat(ANY.intersect(ANY)).isAny(); + assertThat(constraint("[1.5]").intersect(EMPTY)).isEmpty(); assertThat(constraint("[1.5]").intersect(constraint("[1.5]"))).isConstraint("[1.5]"); @@ -355,6 +370,11 @@ public void testIntersect() throws VersionParsingException { @Test public void testUnion() throws VersionParsingException { + assertThat(EMPTY.union(EMPTY)).isEmpty(); + assertThat(EMPTY.union(ANY)).isAny(); + assertThat(ANY.union(EMPTY)).isAny(); + assertThat(ANY.union(ANY)).isAny(); + assertThat(constraint("[1.5]").union(EMPTY)).isConstraint("[1.5]"); assertThat(constraint("[1.5]").union(constraint("[1.5]"))).isConstraint("[1.5]"); @@ -437,9 +457,22 @@ public void testDifference() throws VersionParsingException { .isConstraint("[1.0,2.0),(2.0,3.0),(4.0,6.0)"); assertThat(EMPTY.difference(EMPTY)).isEmpty(); + assertThat(EMPTY.difference(ANY)).isEmpty(); + assertThat(ANY.difference(EMPTY)).isAny(); assertThat(EMPTY.difference(constraint("[1.2,2.0)"))).isEmpty(); } + @Test + public void testComplement() throws VersionParsingException { + assertThat(EMPTY.complement()).isEqualTo(ANY); + assertThat(ANY.complement()).isEqualTo(EMPTY); + assertThat(constraint("[1.5]").complement()).isConstraint("(,1.5),(1.5,)"); + assertThat(constraint("[1.2,2.0)").complement()).isConstraint("(,1.2),[2.0,)"); + assertThat(constraint("[1.5],[2.0,3.0)").complement()).isConstraint("(,1.5),(1.5,2.0),[3.0,)"); + assertThat(constraint("[1.5],[2.0,3.0),[4.0,7.0)").complement()) + .isConstraint("(,1.5),(1.5,2.0),[3.0,4.0),[7.0,)"); + } + @Test public void testEquality() { final VersionConstraint constraint1 = new VersionConstraint(version("1.2.3"));