Skip to content

Commit 64c8454

Browse files
committed
Merge remote-tracking branch 'origin/feature/devonfw#158-version-range-with-open-intervals' into feature/devonfw#103-implement-version-security-checks
# Conflicts: # cli/src/test/java/com/devonfw/tools/ide/version/VersionRangeTest.java
2 parents 7e2023e + 9574f8d commit 64c8454

File tree

5 files changed

+286
-12
lines changed

5 files changed

+286
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.devonfw.tools.ide.version;
2+
3+
/**
4+
* Enum representing the type of interval regarding its boundaries.
5+
*/
6+
public enum BoundaryType {
7+
8+
/** Closed interval - includes the specified values at the boundaries. */
9+
CLOSED,
10+
11+
/** Open interval - excludes the specified values at the boundaries. */
12+
OPEN,
13+
14+
/** Left open interval - excludes the lower bound but includes the upper bound. */
15+
LEFT_OPEN,
16+
17+
/** Right open interval - includes the lower bound but excludes the upper bound. */
18+
RIGHT_OPEN
19+
}

cli/src/main/java/com/devonfw/tools/ide/version/VersionObject.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
/**
44
* Abstract base interface for a version object such as {@link VersionIdentifier} and {@link VersionSegment}.
55
*
6-
*
76
* {@link Comparable} for versions with an extended contract. If two versions are not strictly comparable (e.g.
87
* "1.apple" and "1.banana") we fall back to some heuristics (e.g. lexicographical comparison for
9-
* {@link VersionSegment#getLettersString() letters} that we do not understand (e.g. "apple" < "banana"). Therefore you can
10-
* use {@link #compareVersion(Object)} to get a {@link VersionComparisonResult} that contains the additional information
11-
* as {@link VersionComparisonResult#isUnsafe() unsafe} flag.
8+
* {@link VersionSegment#getLettersString() letters} that we do not understand (e.g. "apple" < "banana"). Therefore, you
9+
* can use {@link #compareVersion(Object)} to get a {@link VersionComparisonResult} that contains the additional
10+
* information as {@link VersionComparisonResult#isUnsafe() unsafe} flag.
1211
*
1312
* @param <T> type of the object to compare (this class itself).
1413
*/

cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java

+134-3
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@
44
import com.fasterxml.jackson.annotation.JsonValue;
55

66
/**
7-
* Container for a range of versions.
7+
* Container for a range of versions. The lower and upper bounds can be exclusive or inclusive. If a bound is null, it
8+
* means that this direction is unbounded. The boolean defining whether this bound is inclusive or exclusive is ignored
9+
* in this case.
810
*/
911
public final class VersionRange implements Comparable<VersionRange> {
1012

1113
private final VersionIdentifier min;
1214

1315
private final VersionIdentifier max;
1416

17+
private final boolean leftIsExclusive;
18+
19+
private final boolean rightIsExclusive;
20+
1521
/**
1622
* The constructor.
1723
*
@@ -24,6 +30,42 @@ public VersionRange(VersionIdentifier min, VersionIdentifier max) {
2430
super();
2531
this.min = min;
2632
this.max = max;
33+
this.leftIsExclusive = false;
34+
this.rightIsExclusive = false;
35+
}
36+
37+
/**
38+
* The constructor.
39+
*
40+
* @param min the {@link #getMin() minimum}.
41+
* @param max the {@link #getMax() maximum}.
42+
* @param boundaryType the {@link BoundaryType} defining whether the boundaries of the range are inclusive or
43+
* exclusive.
44+
*/
45+
public VersionRange(VersionIdentifier min, VersionIdentifier max, BoundaryType boundaryType) {
46+
47+
super();
48+
this.min = min;
49+
this.max = max;
50+
this.leftIsExclusive = BoundaryType.LEFT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType);
51+
this.rightIsExclusive = BoundaryType.RIGHT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType);
52+
}
53+
54+
/**
55+
* The constructor.
56+
*
57+
* @param min the {@link #getMin() minimum}.
58+
* @param max the {@link #getMax() maximum}.
59+
* @param leftIsExclusive - {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise.
60+
* @param rightIsExclusive - {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise.
61+
*/
62+
public VersionRange(VersionIdentifier min, VersionIdentifier max, boolean leftIsExclusive, boolean rightIsExclusive) {
63+
64+
super();
65+
this.min = min;
66+
this.max = max;
67+
this.leftIsExclusive = leftIsExclusive;
68+
this.rightIsExclusive = rightIsExclusive;
2769
}
2870

2971
/**
@@ -44,6 +86,38 @@ public VersionIdentifier getMax() {
4486
return this.max;
4587
}
4688

89+
/**
90+
* @return {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise.
91+
*/
92+
public boolean isLeftExclusive() {
93+
94+
return this.leftIsExclusive;
95+
}
96+
97+
/**
98+
* @return {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise.
99+
*/
100+
public boolean isRightExclusive() {
101+
102+
return this.rightIsExclusive;
103+
}
104+
105+
/**
106+
* @return the {@link BoundaryType} defining whether the boundaries of the range are inclusive or exclusive.
107+
*/
108+
public BoundaryType getBoundaryType() {
109+
110+
if (this.leftIsExclusive && this.rightIsExclusive) {
111+
return BoundaryType.OPEN;
112+
} else if (this.leftIsExclusive) {
113+
return BoundaryType.LEFT_OPEN;
114+
} else if (this.rightIsExclusive) {
115+
return BoundaryType.RIGHT_OPEN;
116+
} else {
117+
return BoundaryType.CLOSED;
118+
}
119+
}
120+
47121
/**
48122
* @param version the {@link VersionIdentifier} to check.
49123
* @return {@code true} if the given {@link VersionIdentifier} is contained in this {@link VersionRange},
@@ -52,11 +126,17 @@ public VersionIdentifier getMax() {
52126
public boolean contains(VersionIdentifier version) {
53127

54128
if (this.min != null) {
129+
if (this.min.equals(version)) {
130+
return !this.leftIsExclusive;
131+
}
55132
if (version.isLess(this.min)) {
56133
return false;
57134
}
58135
}
59136
if (this.max != null) {
137+
if (this.max.equals(version)) {
138+
return !this.rightIsExclusive;
139+
}
60140
if (version.isGreater(this.max)) {
61141
return false;
62142
}
@@ -75,7 +155,37 @@ public int compareTo(VersionRange o) {
75155
}
76156
return -1;
77157
}
78-
return this.min.compareTo(o.min);
158+
int compareMins = this.min.compareTo(o.min);
159+
if (compareMins == 0) {
160+
return this.leftIsExclusive == o.leftIsExclusive ? 0 : this.leftIsExclusive ? 1 : -1;
161+
} else {
162+
return compareMins;
163+
}
164+
}
165+
166+
@Override
167+
public boolean equals(Object obj) {
168+
169+
if (this == obj)
170+
return true;
171+
172+
if (obj == null || getClass() != obj.getClass())
173+
return false;
174+
175+
VersionRange o = (VersionRange) obj;
176+
177+
if (this.min == null && this.max == null) {
178+
return o.min == null && o.max == null;
179+
}
180+
if (this.min == null) {
181+
return o.min == null && this.max.equals(o.max) && this.rightIsExclusive == o.rightIsExclusive;
182+
}
183+
if (this.max == null) {
184+
return this.min.equals(o.min) && o.max == null && this.leftIsExclusive == o.leftIsExclusive;
185+
}
186+
return this.min.equals(o.min) && this.leftIsExclusive == o.leftIsExclusive && this.max.equals(o.max)
187+
&& this.rightIsExclusive == o.rightIsExclusive;
188+
79189
}
80190

81191
@Override
@@ -107,13 +217,15 @@ public boolean equals(Object obj) {
107217
public String toString() {
108218

109219
StringBuilder sb = new StringBuilder();
220+
sb.append(this.leftIsExclusive ? '(' : '[');
110221
if (this.min != null) {
111222
sb.append(this.min);
112223
}
113224
sb.append('>');
114225
if (this.max != null) {
115226
sb.append(this.max);
116227
}
228+
sb.append(this.rightIsExclusive ? ')' : ']');
117229
return sb.toString();
118230
}
119231

@@ -124,10 +236,29 @@ public String toString() {
124236
@JsonCreator
125237
public static VersionRange of(String value) {
126238

239+
boolean leftIsExclusive = false;
240+
boolean rightIsExclusive = false;
241+
242+
if (value.startsWith("(")) {
243+
leftIsExclusive = true;
244+
value = value.substring(1);
245+
}
246+
if (value.startsWith("[")) {
247+
value = value.substring(1);
248+
}
249+
if (value.endsWith(")")) {
250+
rightIsExclusive = true;
251+
value = value.substring(0, value.length() - 1);
252+
}
253+
if (value.endsWith("]")) {
254+
value = value.substring(0, value.length() - 1);
255+
}
256+
127257
int index = value.indexOf('>');
128258
if (index == -1) {
129259
return null; // log warning?
130260
}
261+
131262
VersionIdentifier min = null;
132263
if (index > 0) {
133264
min = VersionIdentifier.of(value.substring(0, index));
@@ -137,7 +268,7 @@ public static VersionRange of(String value) {
137268
if (!maxString.isEmpty()) {
138269
max = VersionIdentifier.of(maxString);
139270
}
140-
return new VersionRange(min, max);
271+
return new VersionRange(min, max, leftIsExclusive, rightIsExclusive);
141272
}
142273

143274
}

cli/src/test/java/com/devonfw/tools/ide/context/AbstractIdeContextTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ protected static IdeTestContext newContext(String projectName, String projectPat
6060
* in that project.
6161
* @return the {@link IdeTestContext} pointing to that project.
6262
*/
63-
protected static IdeTestContext newContext(String projectName, String projectPath, boolean copyForMutation, String ... answers) {
63+
protected static IdeTestContext newContext(String projectName, String projectPath, boolean copyForMutation,
64+
String... answers) {
6465

6566
Path sourceDir = PATH_PROJECTS.resolve(projectName);
6667
Path userDir = sourceDir;

0 commit comments

Comments
 (0)