Skip to content

Commit 0e84aff

Browse files
authored
#158: VersionRange with open boundaries (#159)
1 parent cbe6e88 commit 0e84aff

File tree

4 files changed

+348
-11
lines changed

4 files changed

+348
-11
lines changed
Lines changed: 19 additions & 0 deletions
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

Lines changed: 3 additions & 4 deletions
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

Lines changed: 174 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,58 @@
11
package com.devonfw.tools.ide.version;
22

3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonValue;
5+
36
/**
4-
* 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.
510
*/
611
public final class VersionRange implements Comparable<VersionRange> {
712

813
private final VersionIdentifier min;
914

1015
private final VersionIdentifier max;
1116

17+
private final boolean leftIsExclusive;
18+
19+
private final boolean rightIsExclusive;
20+
21+
private static final String VERSION_SEPARATOR = ">";
22+
23+
private static final String START_EXCLUDING_PREFIX = "(";
24+
25+
private static final String START_INCLUDING_PREFIX = "[";
26+
27+
private static final String END_EXCLUDING_SUFFIX = ")";
28+
29+
private static final String END_INCLUDING_SUFFIX = "]";
30+
31+
public static String getVersionSeparator() {
32+
33+
return VERSION_SEPARATOR;
34+
}
35+
36+
public static String getStartExcludingPrefix() {
37+
38+
return START_EXCLUDING_PREFIX;
39+
}
40+
41+
public static String getStartIncludingPrefix() {
42+
43+
return START_INCLUDING_PREFIX;
44+
}
45+
46+
public static String getEndExcludingSuffix() {
47+
48+
return END_EXCLUDING_SUFFIX;
49+
}
50+
51+
public static String getEndIncludingSuffix() {
52+
53+
return END_INCLUDING_SUFFIX;
54+
}
55+
1256
/**
1357
* The constructor.
1458
*
@@ -20,6 +64,42 @@ public VersionRange(VersionIdentifier min, VersionIdentifier max) {
2064
super();
2165
this.min = min;
2266
this.max = max;
67+
this.leftIsExclusive = false;
68+
this.rightIsExclusive = false;
69+
}
70+
71+
/**
72+
* The constructor.
73+
*
74+
* @param min the {@link #getMin() minimum}.
75+
* @param max the {@link #getMax() maximum}.
76+
* @param boundaryType the {@link BoundaryType} defining whether the boundaries of the range are inclusive or
77+
* exclusive.
78+
*/
79+
public VersionRange(VersionIdentifier min, VersionIdentifier max, BoundaryType boundaryType) {
80+
81+
super();
82+
this.min = min;
83+
this.max = max;
84+
this.leftIsExclusive = BoundaryType.LEFT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType);
85+
this.rightIsExclusive = BoundaryType.RIGHT_OPEN.equals(boundaryType) || BoundaryType.OPEN.equals(boundaryType);
86+
}
87+
88+
/**
89+
* The constructor.
90+
*
91+
* @param min the {@link #getMin() minimum}.
92+
* @param max the {@link #getMax() maximum}.
93+
* @param leftIsExclusive - {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise.
94+
* @param rightIsExclusive - {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise.
95+
*/
96+
public VersionRange(VersionIdentifier min, VersionIdentifier max, boolean leftIsExclusive, boolean rightIsExclusive) {
97+
98+
super();
99+
this.min = min;
100+
this.max = max;
101+
this.leftIsExclusive = leftIsExclusive;
102+
this.rightIsExclusive = rightIsExclusive;
23103
}
24104

25105
/**
@@ -38,6 +118,38 @@ public VersionIdentifier getMax() {
38118
return this.max;
39119
}
40120

121+
/**
122+
* @return {@code true} if the {@link #getMin() minimum} is exclusive, {@code false} otherwise.
123+
*/
124+
public boolean isLeftExclusive() {
125+
126+
return this.leftIsExclusive;
127+
}
128+
129+
/**
130+
* @return {@code true} if the {@link #getMax() maximum} is exclusive, {@code false} otherwise.
131+
*/
132+
public boolean isRightExclusive() {
133+
134+
return this.rightIsExclusive;
135+
}
136+
137+
/**
138+
* @return the {@link BoundaryType} defining whether the boundaries of the range are inclusive or exclusive.
139+
*/
140+
public BoundaryType getBoundaryType() {
141+
142+
if (this.leftIsExclusive && this.rightIsExclusive) {
143+
return BoundaryType.OPEN;
144+
} else if (this.leftIsExclusive) {
145+
return BoundaryType.LEFT_OPEN;
146+
} else if (this.rightIsExclusive) {
147+
return BoundaryType.RIGHT_OPEN;
148+
} else {
149+
return BoundaryType.CLOSED;
150+
}
151+
}
152+
41153
/**
42154
* @param version the {@link VersionIdentifier} to check.
43155
* @return {@code true} if the given {@link VersionIdentifier} is contained in this {@link VersionRange},
@@ -46,12 +158,18 @@ public VersionIdentifier getMax() {
46158
public boolean contains(VersionIdentifier version) {
47159

48160
if (this.min != null) {
49-
if (version.isLess(this.min)) {
161+
VersionComparisonResult compareMin = version.compareVersion(this.min);
162+
if (compareMin.isLess()) {
163+
return false;
164+
} else if (compareMin.isEqual() && this.leftIsExclusive) {
50165
return false;
51166
}
52167
}
53168
if (this.max != null) {
54-
if (version.isGreater(this.max)) {
169+
VersionComparisonResult compareMax = version.compareVersion(this.max);
170+
if (compareMax.isGreater()) {
171+
return false;
172+
} else if (compareMax.isEqual() && this.rightIsExclusive) {
55173
return false;
56174
}
57175
}
@@ -69,33 +187,67 @@ public int compareTo(VersionRange o) {
69187
}
70188
return -1;
71189
}
72-
return this.min.compareTo(o.min);
190+
int compareMins = this.min.compareTo(o.min);
191+
if (compareMins == 0) {
192+
return this.leftIsExclusive == o.leftIsExclusive ? 0 : this.leftIsExclusive ? 1 : -1;
193+
} else {
194+
return compareMins;
195+
}
73196
}
74197

75198
@Override
199+
public boolean equals(Object obj) {
200+
201+
if (this == obj) {
202+
return true;
203+
} else if ((obj == null) || (getClass() != obj.getClass())) {
204+
return false;
205+
}
206+
VersionRange o = (VersionRange) obj;
207+
if (this.min == null && this.max == null) {
208+
return o.min == null && o.max == null;
209+
} else if (this.min == null) {
210+
return o.min == null && this.max.equals(o.max) && this.rightIsExclusive == o.rightIsExclusive;
211+
} else if (this.max == null) {
212+
return this.min.equals(o.min) && o.max == null && this.leftIsExclusive == o.leftIsExclusive;
213+
}
214+
return this.min.equals(o.min) && this.leftIsExclusive == o.leftIsExclusive && this.max.equals(o.max)
215+
&& this.rightIsExclusive == o.rightIsExclusive;
216+
}
217+
218+
@Override
219+
@JsonValue
76220
public String toString() {
77221

78222
StringBuilder sb = new StringBuilder();
223+
sb.append(this.leftIsExclusive ? START_EXCLUDING_PREFIX : START_INCLUDING_PREFIX);
79224
if (this.min != null) {
80225
sb.append(this.min);
81226
}
82-
sb.append('>');
227+
sb.append(VERSION_SEPARATOR);
83228
if (this.max != null) {
84229
sb.append(this.max);
85230
}
231+
sb.append(this.rightIsExclusive ? END_EXCLUDING_SUFFIX : END_INCLUDING_SUFFIX);
86232
return sb.toString();
87233
}
88234

89235
/**
90236
* @param value the {@link #toString() string representation} of a {@link VersionRange} to parse.
91237
* @return the parsed {@link VersionRange}.
92238
*/
239+
@JsonCreator
93240
public static VersionRange of(String value) {
94241

95-
int index = value.indexOf('>');
242+
boolean leftIsExclusive = value.startsWith(START_EXCLUDING_PREFIX);
243+
boolean rightIsExclusive = value.endsWith(END_EXCLUDING_SUFFIX);
244+
value = removeAffixes(value);
245+
246+
int index = value.indexOf(VERSION_SEPARATOR);
96247
if (index == -1) {
97248
return null; // log warning?
98249
}
250+
99251
VersionIdentifier min = null;
100252
if (index > 0) {
101253
min = VersionIdentifier.of(value.substring(0, index));
@@ -105,7 +257,22 @@ public static VersionRange of(String value) {
105257
if (!maxString.isEmpty()) {
106258
max = VersionIdentifier.of(maxString);
107259
}
108-
return new VersionRange(min, max);
260+
return new VersionRange(min, max, leftIsExclusive, rightIsExclusive);
261+
}
262+
263+
private static String removeAffixes(String value) {
264+
265+
if (value.startsWith(START_EXCLUDING_PREFIX)) {
266+
value = value.substring(START_EXCLUDING_PREFIX.length());
267+
} else if (value.startsWith(START_INCLUDING_PREFIX)) {
268+
value = value.substring(START_INCLUDING_PREFIX.length());
269+
}
270+
if (value.endsWith(END_EXCLUDING_SUFFIX)) {
271+
value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length());
272+
} else if (value.endsWith(END_INCLUDING_SUFFIX)) {
273+
value = value.substring(0, value.length() - END_EXCLUDING_SUFFIX.length());
274+
}
275+
return value;
109276
}
110277

111278
}

0 commit comments

Comments
 (0)